Skip to content
This repository has been archived by the owner on May 13, 2022. It is now read-only.

Using the JoinMarket internal wallet

chris-belcher edited this page Oct 5, 2015 · 27 revisions

For many JoinMarket applications, like the tumbler, yield generator and patient send payments, an internal wallet is required. The wallet is hierarchical and deterministic, it can be entirely recovered from a single seed. The internal wallet is not required for the Electrum plugin. Use --help on the command line for all options.

Contents

## Creating a Wallet Run wallet-tool.py with the 'generate' method. Write down the 12 word seed on paper.
$ python wallet-tool.py generate
Write down this wallet recovery seed

upon upon release grace led brain skill cost back clothes bump trouble

Enter wallet encryption passphrase: 
Reenter wallet encryption passphrase: 
Input wallet file name (default: wallet.json): 
saved to wallet.json
$
## Funding Wallet and Displaying Balance Run wallet-tool.py with the name of your wallet file as first argument. Bitcoins should be sent to empty receive addresses. Empty change addresses are hidden by default. Using 'displayall' as the second argument will show all addresses in wallet, including empty used ones.
$ python wallet-tool.py wallet.json
Enter wallet decryption passphrase: 
[2015/04/23 02:04:13] downloading wallet history
[2015/04/23 02:04:26] blockr sync_unspent took 2.70895719528sec
mixing depth 0 m/0/0/
 receive addresses m/0/0/0/
  m/0/0/0/000 1JPFmg1RSa2gtzcsow9fBjwdvWPsxcP3eX  new 0.00000000 btc 
  m/0/0/0/001 1AaCpeMit59ExfSvP3M3bTnMkhXgecSPeY  new 0.00000000 btc 
  m/0/0/0/002 1NmDrVbtk6kfAYbBVo7Miv8eCYHHefZkjs  new 0.00000000 btc 
  m/0/0/0/003 1NKitLXm7FdgbHuENvFXRCxVH32N5XXMQ5  new 0.00000000 btc 
  m/0/0/0/004 1EwkvF8SrHLh17LKCNQ9w4u4HY2akuzhx3  new 0.00000000 btc 
  m/0/0/0/005 1HkHyB8DbZBNvZYwyAutgedaBSsrNUDt7G  new 0.00000000 btc 
 change addresses m/0/0/1/
for mixdepth=0 balance=0.00000000btc
mixing depth 1 m/0/1/
 receive addresses m/0/1/0/
  m/0/1/0/000 1LQw8K7V2KQePFVscLKiiH1NU2v6KzwdhW  new 0.00000000 btc 
  m/0/1/0/001 1EcZ7w1EEb1UK1qWYT6FMLsbRoizFCfAZ7  new 0.00000000 btc 
  m/0/1/0/002 1CV7L2b23sEYNhnu35MP9gbzPAD3j3ofgc  new 0.00000000 btc 
  m/0/1/0/003 1DMYRugQNJZRQPcAPAYBcE1p9u15VFTkD9  new 0.00000000 btc 
  m/0/1/0/004 1CCnPgGhecXmFz8DrB3Wew9kHT1En53Lq   new 0.00000000 btc 
  m/0/1/0/005 1LuwwyEv86BV4miaKVScsFxE4rrKngVt8F  new 0.00000000 btc 
 change addresses m/0/1/1/
for mixdepth=1 balance=0.00000000btc
$
## Recovering a Wallet from mnemonic In the event of loss of encrypted wallet file, use the 12 word seed to recover by running wallet-tool.py with 'recover' as first argument.
$ python wallet-tool.py recover
Input 12 word recovery seed: upon upon release grace led brain skill cost back clothes bump trouble
5e65114c8db9a6555a95d925959c1b18
Enter wallet encryption passphrase: 
Reenter wallet encryption passphrase: 
Input wallet file name (default: wallet.json): 
saved to wallet.json
$

Help! I recovered but some of my money is missing

Try increasing the gap limit up from its default of 6. This likely happened because you were running a yield-generator bot and somebody (poorly) attempted to DOS you.

$ python wallet-tool.py -g 50 my-wallet-file.json
## Recovering Private Keys In the event of bitcoins stuck in the wallet use the -p command line flag to print out private keys, which can be imported into another wallet.
$ python wallet-tool.py -p wallet.json
Enter wallet decryption passphrase: 
[2015/04/23 02:02:22] downloading wallet history
[2015/04/23 02:02:34] blockr sync_unspent took 2.29012322426sec
mixing depth 0 m/0/0/
 receive addresses m/0/0/0/
  m/0/0/0/000 1JPFmg1RSa2gtzcsow9fBjwdvWPsxcP3eX  new 0.00000000 btc Ky1bG7ba51yUE8rfvTTTUyZr1z4aKJEdfAbUo5iDLoTQK8HSJZNR
  m/0/0/0/001 1AaCpeMit59ExfSvP3M3bTnMkhXgecSPeY  new 0.00000000 btc L3eKPoMrHbfK96Lp3jZNJgxzUyqceJ4JwD9dZ8hqLSxp2hrHwnCb
  m/0/0/0/002 1NmDrVbtk6kfAYbBVo7Miv8eCYHHefZkjs  new 0.00000000 btc KyUZem3yazQT1hw4tVPP3Z6HG2QJh2hStkYQwpT33PzmhcQFPFPg
  m/0/0/0/003 1NKitLXm7FdgbHuENvFXRCxVH32N5XXMQ5  new 0.00000000 btc L3wPhBrxMFZraqBsRqt54uijDDvTtqejgSHJfQqD7pyDbZcq7HGv
  m/0/0/0/004 1EwkvF8SrHLh17LKCNQ9w4u4HY2akuzhx3  new 0.00000000 btc Kx95MLcfSgEG3jdXZRS9uTF7aVXMGtGoVF26s4p8g6rMNPrAx5ok
  m/0/0/0/005 1HkHyB8DbZBNvZYwyAutgedaBSsrNUDt7G  new 0.00000000 btc L1QezwwFenmEG3kxmrVBBywdNWHVee3PcFqC27CJb1F4ZvaotQcT
 change addresses m/0/0/1/
for mixdepth=0 balance=0.00000000btc
mixing depth 1 m/0/1/
 receive addresses m/0/1/0/
  m/0/1/0/000 1LQw8K7V2KQePFVscLKiiH1NU2v6KzwdhW  new 0.00000000 btc KyH8CqoqeAthf9HUxGdGQfrkZJoTtneMwbbrYqfiAn5fwSqfkXSz
  m/0/1/0/001 1EcZ7w1EEb1UK1qWYT6FMLsbRoizFCfAZ7  new 0.00000000 btc KyZY1p2MHWjVFxk2uePtbaUi64tWDA43AjjRskCMW49bgKypELjG
  m/0/1/0/002 1CV7L2b23sEYNhnu35MP9gbzPAD3j3ofgc  new 0.00000000 btc L1pgR97neuXewxhtU58XyWBdByeg6cNPqLjWcwAdKT612GDoCTTC
  m/0/1/0/003 1DMYRugQNJZRQPcAPAYBcE1p9u15VFTkD9  new 0.00000000 btc L2ePgHdi5gw31djaLusnWesdMtFoLuJr4ag1sHXc6X9meCZT9ycc
  m/0/1/0/004 1CCnPgGhecXmFz8DrB3Wew9kHT1En53Lq   new 0.00000000 btc KyppEz25HyegaAycjwrbUJ4F2B2Cic2BL5GYDtiSrwGqTXoyyrmJ
  m/0/1/0/005 1LuwwyEv86BV4miaKVScsFxE4rrKngVt8F  new 0.00000000 btc L4ecJDUNcK8jv7NNynKUbv6XeCbCWkG78LAvLxv6mVcnGmLdCn1X
 change addresses m/0/1/1/
for mixdepth=1 balance=0.00000000btc
$
## BIP32 wallet structure ### The Mixing Depth Concept

The point of JoinMarket is to improve privacy. Merged transaction inputs are damaging to privacy because they provide evidence of common ownership. Each mixing depth is a different identity, coins are never merged in the same transaction across mixing depths, but may be merged within mixing depths. Coins move between mixing depths through coinjoins. A change output stays in the same mixing depth. This prevents the situation where a change output is merged with a coinjoin output in a later transaction, which would render the coinjoin easily unmixable.

An example of the different identities being used is to not leak a lower limit of your wallet balance. Imagine if someone pays you $10 and sees it combined with $1m, they could deduce you own at least that much. If instead those two payments go to different mixing levels then this analysis becomes harder.

Structure

m - generated from seed
m/0 - joinmarket root
m/0/n - nth mixing depth
m/0/n/0/k - kth receive address, for mixing depth n
m/0/n/1/k - kth change address, for mixing depth n
## What is the Gap Limit?

With a deterministic wallet you create a sequence of bitcoin addresses from an initial seed. Imagine counting 1, 2, 3, 4, etc.

You can create as many addresses as you like, but not all of them will appear the blockchain. For instance I might create one especially for you to give me 1,000,000 BTC. That is (alas!) probably not going to be used so will likely never appear on the blockchain.

When you are starting JoinMarket it does not know which is the last address used. So you start at the beginning and see what is on the blockchain. Then you look for the next one in the sequence. The gap limit is how many misses you accept before you give up and stop looking. The same concept is used in other deterministic wallets like Electrum or Armoury.