mirror of
https://github.com/JoinMarket-Org/joinmarket-clientserver.git
synced 2026-08-13 12:33:40 +02:00
Add usage guide for fidelity bond wallets
This commit is contained in:
parent
2860c4fc9f
commit
14f086bfe4
1 changed files with 271 additions and 0 deletions
271
docs/fidelity-bonds.md
Normal file
271
docs/fidelity-bonds.md
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
# Fidelity bonds
|
||||
|
||||
Fidelity bonds are a feature of JoinMarket which improves the resistance to
|
||||
sybil attacks, and therefore improves the privacy of the system.
|
||||
|
||||
## This feature is incomplete and so is disabled for now
|
||||
|
||||
A fidelity bond is a mechanism where bitcoin value is deliberately sacrificed
|
||||
to make a cryptographic identity expensive to obtain. The sacrifice is done in
|
||||
a way that can be proven to a third party. Takers in JoinMarket will
|
||||
have a higher probability to create coinjoins with makers who publish more
|
||||
valuable fidelity bonds. This has the effect of making the system much more
|
||||
expensive to sybil attack, because an attacker would have to sacrifice a lot of
|
||||
value in order to be chosen very often by takers when creating coinjoin.
|
||||
|
||||
As a maker you can take part in many more coinjoins and therefore earn more
|
||||
fees if you sacrifice bitcoin value to create a fidelity bond. The most
|
||||
practical way to create a fidelity bond is to send bitcoins to a time-locked
|
||||
address which uses the opcode [OP_CHECKLOCKTIMEVERIFY](https://en.bitcoin.it/wiki/Timelock#CheckLockTimeVerify).
|
||||
The valuable thing being sacrificed is the time-value-of-money. Note that a
|
||||
long-term holder (or hodler) of bitcoins can buy time-locked fidelity bonds
|
||||
essentially for free, assuming they never intended to transact with their coins
|
||||
anyway.
|
||||
|
||||
Another way to create fidelity bonds is to destroy coins by sending them to a
|
||||
[OP_RETURN](https://en.bitcoin.it/wiki/Script#Provably_Unspendable.2FPrunable_Outputs)
|
||||
output.
|
||||
|
||||
The private keys to fidelity bonds can be kept in [cold storage](https://en.bitcoin.it/wiki/Cold_storage)
|
||||
for added security.
|
||||
|
||||
For a more detailed explanation of how fidelity bonds work see these documents:
|
||||
|
||||
* [Design for improving JoinMarket's resistance to sybil attacks using fidelity
|
||||
bonds](https://gist.github.com/chris-belcher/18ea0e6acdb885a2bfbdee43dcd6b5af/)
|
||||
|
||||
* [Financial mathematics of JoinMarket fidelity bonds](https://gist.github.com/chris-belcher/87ebbcbb639686057a389acb9ab3e25b)
|
||||
|
||||
#### Note on privacy
|
||||
|
||||
Bitcoin outputs which create fidelity bonds will be published to the entire
|
||||
world, so before and after creating them make sure the outputs are not linked
|
||||
to your identity in any way. Perhaps mix with JoinMarket before and after.
|
||||
|
||||
### Creating a JoinMarket wallet which supports fidelity bonds
|
||||
|
||||
When generating a JoinMarket wallet on the command line, supporting versions
|
||||
will offer an option to make the wallet support fidelity bonds.
|
||||
|
||||
(jmvenv) $ python3 wallet-tool.py generate
|
||||
Would you like to use a two-factor mnemonic recovery phrase? write 'n' if you don't know what this is (y/n): n
|
||||
Not using mnemonic extension
|
||||
Enter wallet file encryption passphrase:
|
||||
Reenter wallet file encryption passphrase:
|
||||
Input wallet file name (default: wallet.jmdat): testfidelity.jmdat
|
||||
Would you like this wallet to support fidelity bonds? write 'n' if you don't know what this is (y/n): y
|
||||
Write down this wallet recovery mnemonic
|
||||
|
||||
evidence initial knee image inspire plug dad midnight blast awkward clean between
|
||||
|
||||
Generated wallet OK
|
||||
|
||||
As always, it is crucially important to write down the 12-word [seed phrase](https://en.bitcoin.it/wiki/Seed_phrase)
|
||||
as a backup. It is also recommended to write down the name of the creating wallet
|
||||
"JoinMarket" and that the fidelity bond option was enabled. Writing the wallet
|
||||
creation date is also useful as it can help with rescanning.
|
||||
|
||||
### Obtaining time-locked addresses
|
||||
|
||||
The `wallet-tool.py` script supports a new method `gettimelockaddress` used for
|
||||
obtaining time-locked addresses. If coins are sent to these addresses they will
|
||||
be locked up until the timelock date passes. Only mixdepth zero can have
|
||||
fidelity bonds in it.
|
||||
|
||||
This example creates an address which locks any coins sent to it until April 2020.
|
||||
|
||||
(jmvenv) $ python3 wallet-tool.py testfidelity.jmdat gettimelockaddress 2020-4
|
||||
Enter wallet decryption passphrase:
|
||||
path = m/49'/1'/0'/2/3:1585699200
|
||||
Coins sent to this address will be not be spendable until April 2020. Full date: 2020-04-01 00:00:00
|
||||
bcrt1qrc2qu3m2l2spayu5kr0k0rnn9xgjz46zsxmruh87a3h3f5zmnkaqlfx7v5
|
||||
|
||||
If coins are sent to these addresses they will appear in the usual `wallet-tool.py`
|
||||
display:
|
||||
|
||||
(jmvenv) $ python3 wallet-tool.py -m 0 testfidelity.jmdat
|
||||
Enter wallet decryption passphrase:
|
||||
JM wallet
|
||||
mixdepth 0 fbonds-mpk-tpubDDCbCPdf5wJVGYWB4mZr3E3Lys4NBcEKysrrUrLfhG6sekmrvs6KZNe4i5p5z3FyfwRmKMqB9NWEcEUiTS4LwqfrKPQzhKj6aLihu2EejaU
|
||||
external addresses m/49'/1'/0'/0 tpubDEGdmPwmQRcZmGKhaudjch9Fgw4J5yP4bYw5B8LoSDkMdmhBxM4ndEQXHK4r1TPexGjLidxdpeEzsJcdXEe7khWToxCZuN6JiLzvUoHAki2
|
||||
m/49'/1'/0'/0/0 2N8jHuQaApgFtQ8UKxKbREAvNxKn4BGX4x2 0.00000000 new
|
||||
m/49'/1'/0'/0/1 2Mx5CwDoNcuCT38EDmgenQxv9skHbZfXFdo 0.00000000 new
|
||||
m/49'/1'/0'/0/2 2N1tNTTwNyucGGmfDWNVk3AUi3i5S8jVKqn 0.00000000 new
|
||||
m/49'/1'/0'/0/3 2N8eBEU5wpWb6kS1gvbRgewtxsmXsMkShV6 0.00000000 new
|
||||
m/49'/1'/0'/0/4 2MuHgeSgMsvkcn6aGNW2uk2UXP3xVVnkfh2 0.00000000 new
|
||||
m/49'/1'/0'/0/5 2NA8d8um5KmBNNR8dadhbEDYGiTJPFCdjMB 0.00000000 new
|
||||
Balance: 0.00000000
|
||||
internal addresses m/49'/1'/0'/1
|
||||
Balance: 0.00000000
|
||||
internal addresses m/49'/1'/0'/2 tpubDEGdmPwmQRcZrzjRmUFqXXyLdRedwxCWQviAFqDe6sXJeZzRNTwmwqMfxN6Ka3v7hEebstrU5kqUNoHsFKaA3RoB2vopL6kLHVo1EQq6USw
|
||||
m/49'/1'/0'/2/0:1585699200 bcrt1qrc2qu3m2l2spayu5kr0k0rnn9xgjz46zsxmruh87a3h3f5zmnkaqlfx7v5 0.15000000 2020-04-01 [LOCKED]
|
||||
Balance: 0.15000000
|
||||
Balance for mixdepth 0: 0.15000000
|
||||
Total balance: 0.15000000
|
||||
|
||||
#### Spending time-locked coins
|
||||
|
||||
Once the time-lock of an address expires the coins can be spent with JoinMarket.
|
||||
|
||||
Coins living on time-locked addresses are automatically frozen with
|
||||
JoinMarket's coin control feature, so before spending you need to unfreeze the
|
||||
coins using `python3 wallet-tool.py <walletname> -m 0 freeze`.
|
||||
|
||||
Once unfrozen and untimelocked the coins can be spent normally with the scripts
|
||||
`sendpayment.py`, `tumber.py`, or yield generator.
|
||||
|
||||
### Burning coins
|
||||
|
||||
Coins can be burned with a special method of the `sendpayment.py` script. Set
|
||||
the destination to be `BURN`. Transactions which burn coins must only have one
|
||||
input and one output, so use coin control to freeze all coins in the zeroth
|
||||
mixdepth except one.
|
||||
|
||||
$ python3 sendpayment.py -N 0 testfidelity3.jmdat 0 BURN
|
||||
User data location: .
|
||||
2020-04-07 20:45:25,658 [INFO] Using this min relay fee as tx fee floor: 1000 sat/vkB (1.0 sat/vB)
|
||||
Enter wallet decryption passphrase:
|
||||
2020-04-07 20:46:50,449 [INFO] Estimated miner/tx fees for this coinjoin amount: 0.0%
|
||||
2020-04-07 20:46:50,452 [INFO] Using this min relay fee as tx fee floor: 1000 sat/vkB (1.0 sat/vB)
|
||||
2020-04-07 20:46:50,452 [INFO] Using a fee of : 0.00000200 BTC (200 sat).
|
||||
2020-04-07 20:46:50,454 [INFO] Got signed transaction:
|
||||
|
||||
2020-04-07 20:46:50,455 [INFO] {'ins': [{'outpoint': {'hash': '61d69b4e7abe0ef8a5a9cbabb05463259c3b497a142130a56f81a9259f048cb0',
|
||||
'index': 0},
|
||||
'script': '160014295beb4eba9b35896683d5b5ff455ee2c646054c',
|
||||
'sequence': 4294967294,
|
||||
'txinwitness': ['3045022100939de908e30015c6b22d2c0f25153e395268466ce44eeeb4ec03a8920440e87b0220155d1c43dedb3fc2654205541bb2821dd5211180e2d7f93d67301470652830d401',
|
||||
'03ec0f8b267f99ff5259195ce63813d58f38ffbaada894ce06af0c0303c74bbf82']}],
|
||||
'locktime': 1361,
|
||||
'outs': [{'script': '6a147631c805d8ad9239677b8d7530353fda3fec07ca',
|
||||
'value': 11999800}],
|
||||
'version': 2}
|
||||
2020-04-07 20:46:50,455 [INFO] In serialized form (for copy-paste):
|
||||
2020-04-07 20:46:50,455 [INFO] 02000000000101b08c049f25a9816fa53021147a493b9c256354b0abcba9a5f80ebe7a4e9bd6610000000017160014295beb4eba9b35896683d5b5ff455ee2c646054cfeffffff01381ab70000000000166a147631c805d8ad9239677b8d7530353fda3fec07ca02483045022100939de908e30015c6b22d2c0f25153e395268466ce44eeeb4ec03a8920440e87b0220155d1c43dedb3fc2654205541bb2821dd5211180e2d7f93d67301470652830d4012103ec0f8b267f99ff5259195ce63813d58f38ffbaada894ce06af0c0303c74bbf8251050000
|
||||
2020-04-07 20:46:50,456 [INFO] Sends: 0.11999800 BTC (11999800 sat) to destination: BURNER OUTPUT embedding pubkey at m/49'/1'/0'/3/0
|
||||
|
||||
WARNING: This transaction if broadcasted will PERMANENTLY DESTROY your bitcoins
|
||||
|
||||
Would you like to push to the network? (y/n):y
|
||||
2020-04-07 20:47:52,047 [DEBUG] rpc: sendrawtransaction ['02000000000101b08c049f25a9816fa53021147a493b9c256354b0abcba9a5f80ebe7a4e9bd6610000000017160014295beb4eba9b35896683d5b5ff455ee2c646054cfeffffff01381ab70000000000166a147631c805d8ad9239677b8d7530353fda3fec07ca02483045022100939de908e30015c6b22d2c0f25153e395268466ce44eeeb4ec03a8920440e87b0220155d1c43dedb3fc2654205541bb2821dd5211180e2d7f93d67301470652830d4012103ec0f8b267f99ff5259195ce63813d58f38ffbaada894ce06af0c0303c74bbf8251050000']
|
||||
2020-04-07 20:47:52,049 [WARNING] Connection had broken pipe, attempting reconnect.
|
||||
2020-04-07 20:47:52,440 [INFO] Transaction sent: 656bb4538f14f2cc874043915907b6c9c46a807ef9818bde771d07630d54b0f7
|
||||
done
|
||||
|
||||
Embedded in the `OP_RETURN` output is the hash of a pubkey from the wallet.
|
||||
|
||||
Now `OP_RETURN` outputs are not addresses, and because of technical reasons the
|
||||
first time they are synchronized the flag `--recoversync` must be used. When
|
||||
this is done the burn output will appear in the `wallet-tool.py` display.
|
||||
`--recoversync` only needs to be used once, and after that the burner output is
|
||||
saved in the `wallet.jmdat` file which can be accesses by all future
|
||||
synchronizations.
|
||||
|
||||
$ python3 wallet-tool.py --datadir=. --recoversync testfidelity2.jmdat
|
||||
Enter wallet decryption passphrase:
|
||||
2020-04-07 23:09:54,644 [INFO] Found a burner transaction txid=656bb4538f14f2cc874043915907b6c9c46a807ef9818bde771d07630d54b0f7 path = m/49'/1'/0'/3/0
|
||||
2020-04-07 23:09:54,769 [WARNING] Merkle branch not available, use wallet-tool `addtxoutproof`
|
||||
2020-04-07 23:09:55,420 [INFO] Found a burner transaction txid=656bb4538f14f2cc874043915907b6c9c46a807ef9818bde771d07630d54b0f7 path = m/49'/1'/0'/3/0
|
||||
2020-04-07 23:09:55,422 [WARNING] Merkle branch not available, use wallet-tool `addtxoutproof`
|
||||
JM wallet
|
||||
mixdepth 0 fbonds-mpk-tpubDDCXgSpdxuVbXgzRYBggFeMRNeV9eH24jJuQNunyqwYtDFiB7ZS63LhXwHkf7o9ZcZW4qUz7uvD6yk4BkkF3bBPmJRPv7RBTEA5hHwEdV2f
|
||||
external addresses m/49'/1'/0'/0 tpubDEJGorVywRb6LoLQbaqWZh2gYwpdZqViCNZ2ejB5kpBuUp16LHpK6ESFaJLixidtbmmjcDwVZ4QjnAbKmypfuGaEk3ifgonPv4vsugqgp9G
|
||||
m/49'/1'/0'/0/2 2MviB2FfLKZjFb3W2dJ8kXcQBj3jqMJg7TL 0.00000000 new
|
||||
m/49'/1'/0'/0/3 2MtsAQhE2u9VGV3aZ7XM4PzwWGHXr4PAhqP 0.00000000 new
|
||||
m/49'/1'/0'/0/4 2N3iXNjS4vkTXzy5Jidnovc6FJeNQJx5Fnt 0.00000000 new
|
||||
m/49'/1'/0'/0/5 2MtT4XAjwQQ7PBbrTxv7qcQMMmz4Rs5XrE3 0.00000000 new
|
||||
m/49'/1'/0'/0/6 2NEuG23BQESuZTqSDtab9zYsd1Jb4KfMULB 0.00000000 new
|
||||
m/49'/1'/0'/0/7 2N6NGJRX6KYQWtYWK8iHFuJNpZRs8NbUAC9 0.00000000 new
|
||||
Balance: 0.00000000
|
||||
internal addresses m/49'/1'/0'/1
|
||||
Balance: 0.00000000
|
||||
internal addresses m/49'/1'/0'/2 tpubDEJGorVywRb6T34X7ZAEz9hQYn6CCEhrcFa8kA2mqNau2DvoggZP2QTtXRe8t9NSfMkx3ye8QDzqCE9gEqso6fw5ALk5xycWLFwTRLSqSUV
|
||||
Balance: 0.00000000
|
||||
internal addresses m/49'/1'/0'/3 tpubDEJGorVywRb6V5em9Q7LFJ9eLEAEZmZxUdDmkknrKNUs7vKcCWPKwP8YPjuxFCCXk2F1wJnubNbmgbtWed5yiE3D1qxzLonVuXT6QEZPaof
|
||||
m/49'/1'/0'/3/0 BURN-7631c805d8ad9239677b8d7530353fda3fec07ca 0.11999800 656bb4538f14f2cc874043915907b6c9c46a807ef9818bde771d07630d54b0f7 [NO MERKLE PROOF]
|
||||
Balance: 0.11999800
|
||||
Balance for mixdepth 0: 0.11999800
|
||||
Total balance: 0.11999800
|
||||
|
||||
#### Adding the merkle proof of a burn transaction if missing
|
||||
|
||||
In order to prove a burn output exists, a merkle proof is needed. If the Core
|
||||
node is pruned and the block deleted then JoinMarket will not be able to obtain
|
||||
the merkle proof (as in the above example). In this case the proof can be
|
||||
added separately.
|
||||
|
||||
Any other unpruned Core node can trustlessly obtain the proof and give it to
|
||||
the user with the RPC call `gettxoutproof`.
|
||||
|
||||
First obtain the merkle proof:
|
||||
|
||||
$ bitcoin-cli gettxoutproof "[\"656bb4538f14f2cc874043915907b6c9c46a807ef9818bde771d07630d54b0f7\"]" 4cce28f4eb1ea1762ec4ceb90529b3ab28c0423ac630c6292319e2b2712daada
|
||||
0000002056e4050f54084a1d6e6944b209cce76ebe2da4b37f3aa47ab6c612831d3220471015e80d3050cf0ee05b216036030fe3d4906221196943a30e828741ad4cfaeb05d98c5effff7f20010000000200000002a5910e5cf4e6cb6d55e1e2ca979987772a482e8a8f30b7a6cab8c5671f5c161df7b0540d63071d77de8b81f97e806ac4c9b6075991434087ccf2148f53b46b650105
|
||||
|
||||
Then add it to the JoinMarket wallet:
|
||||
|
||||
$ python3 wallet-tool.py -H "m/49'/1'/0'/3/0" testfidelity2.jmdat addtxoutproof 0000002056e4050f54084a1d6e6944b209cce76ebe2da4b37f3aa47ab6c612831d3220471015e80d3050cf0ee05b216036030fe3d4906221196943a30e828741ad4cfaeb05d98c5effff7f20010000000200000002a5910e5cf4e6cb6d55e1e2ca979987772a482e8a8f30b7a6cab8c5671f5c161df7b0540d63071d77de8b81f97e806ac4c9b6075991434087ccf2148f53b46b650105
|
||||
Enter wallet decryption passphrase:
|
||||
Done
|
||||
|
||||
The `-H` flag must point to the path containing the burn output.
|
||||
|
||||
Then synchronizing the wallet won't output the no-merkle-proof warning.
|
||||
|
||||
### Creating watch-only fidelity bond wallets
|
||||
|
||||
Fidelity bonds can be held on an offline computer in
|
||||
[cold storage](https://en.bitcoin.it/wiki/Cold_storage). To do this we create
|
||||
a watch-only fidelity bond wallet.
|
||||
|
||||
When fidelity bonds are displayed in `wallet-tool.py`, their master public key
|
||||
is highlighted with a prefix `fbonds-mpk-`.
|
||||
|
||||
This master public key can be used to create a watch-only wallet using
|
||||
`wallet-tool.py`.
|
||||
|
||||
$ python3 wallet-tool.py createwatchonly fbonds-mpk-tpubDDCbCPdf5wJVGYWB4mZr3E3Lys4NBcEKysrrUrLfhG6sekmrvs6KZNe4i5p5z3FyfwRmKMqB9NWEcEUiTS4LwqfrKPQzhKj6aLihu2EejaU
|
||||
Input wallet file name (default: watchonly.jmdat): watchfidelity.jmdat
|
||||
Enter wallet file encryption passphrase:
|
||||
Reenter wallet file encryption passphrase:
|
||||
Done
|
||||
|
||||
Then the wallet can be displayed like a regular wallet, although only the zeroth
|
||||
mixdepth will be shown.
|
||||
|
||||
$ python3 wallet-tool.py watchfidelity.jmdat
|
||||
User data location: .
|
||||
Enter wallet decryption passphrase:
|
||||
JM wallet
|
||||
mixdepth 0 fbonds-mpk-tpubDDCbCPdf5wJVGYWB4mZr3E3Lys4NBcEKysrrUrLfhG6sekmrvs6KZNe4i5p5z3FyfwRmKMqB9NWEcEUiTS4LwqfrKPQzhKj6aLihu2EejaU
|
||||
external addresses m/49'/1'/0'/0 tpubDEGdmPwmQRcZmGKhaudjch9Fgw4J5yP4bYw5B8LoSDkMdmhBxM4ndEQXHK4r1TPexGjLidxdpeEzsJcdXEe7khWToxCZuN6JiLzvUoHAki2
|
||||
m/49'/1'/0'/0/0 2N8jHuQaApgFtQ8UKxKbREAvNxKn4BGX4x2 0.00000000 used
|
||||
m/49'/1'/0'/0/1 2Mx5CwDoNcuCT38EDmgenQxv9skHbZfXFdo 0.00000000 new
|
||||
m/49'/1'/0'/0/2 2N1tNTTwNyucGGmfDWNVk3AUi3i5S8jVKqn 0.00000000 new
|
||||
m/49'/1'/0'/0/3 2N8eBEU5wpWb6kS1gvbRgewtxsmXsMkShV6 0.00000000 new
|
||||
m/49'/1'/0'/0/4 2MuHgeSgMsvkcn6aGNW2uk2UXP3xVVnkfh2 0.00000000 new
|
||||
m/49'/1'/0'/0/5 2NA8d8um5KmBNNR8dadhbEDYGiTJPFCdjMB 0.00000000 new
|
||||
m/49'/1'/0'/0/6 2NG76BAHPccfyy6sH68EHrB9QJBycx3FKb6 0.00000000 new
|
||||
Balance: 0.25000000
|
||||
internal addresses m/49'/1'/0'/1
|
||||
Balance: 0.00000000
|
||||
internal addresses m/49'/1'/0'/2 tpubDEGdmPwmQRcZrzjRmUFqXXyLdRedwxCWQviAFqDe6sXJeZzRNTwmwqMfxN6Ka3v7hEebstrU5kqUNoHsFKaA3RoB2vopL6kLHVo1EQq6USw
|
||||
m/49'/1'/0'/0/3:1585699200 bcrt1qrc2qu3m2l2spayu5kr0k0rnn9xgjz46zsxmruh87a3h3f5zmnkaqlfx7v5 0.15000000 2020-04-01 [UNLOCKED]
|
||||
Balance: 0.15000000
|
||||
internal addresses m/49'/1'/0'/3 tpubDEGdmPwmQRcZuX3uNrCouu5bRgp2GJcoQTvhkFAJMTA3yxhKmQyeGwecbnkms4DYmBhCJn2fGTuejTe3g8oyJW3qKcfB4b3Swj2hDk1h4Y2
|
||||
Balance: 0.00000000
|
||||
Balance for mixdepth 0: 0.15000000
|
||||
Total balance: 0.15000000
|
||||
|
||||
### BIP32 Paths
|
||||
|
||||
Fidelity bond wallets extend the BIP32 path format to include the locktime
|
||||
values. In this example we've got `m/49'/1'/0'/2/0:1583020800` where the
|
||||
number after the colon is the locktime value in Unix time.
|
||||
|
||||
This path can be passed to certain wallet methods like `dumpprivkey`.
|
||||
|
||||
$ python3 wallet-tool.py -H "m/49'/1'/0'/2/0:1583020800" testfidelity.jmdat dumpprivkey
|
||||
Enter wallet decryption passphrase:
|
||||
cNEuE5ypNTxVFCyC5iH7u5AQTrddamcUHRPNweiLvmHUWd6XXDkz
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue