forked from ElementsProject/elements
-
Notifications
You must be signed in to change notification settings - Fork 0
/
assets_tutorial.sh
executable file
·348 lines (256 loc) · 11.4 KB
/
assets_tutorial.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
## Preparations
# First we need to set up our config files to walk through this demo
# Let's have some testing user directories for 1 bitcoin node and 2 elements nodes.
rm -r ~/bitcoindir ; rm -r ~/elementsdir1 ; rm -r ~/elementsdir2
mkdir ~/bitcoindir ; mkdir ~/elementsdir1 ; mkdir ~/elementsdir2
# Also configure the nodes by copying the configuration files from
# this directory (and read them):
cp ./contrib/assets_tutorial/bitcoin.conf ~/bitcoindir/bitcoin.conf
cp ./contrib/assets_tutorial/elements1.conf ~/elementsdir1/elements.conf
cp ./contrib/assets_tutorial/elements2.conf ~/elementsdir2/elements.conf
# Set some aliases:
cd src
shopt -s expand_aliases
ELEMENTSPATH="."
BITCOINPATH="."
alias e1-cli="$ELEMENTSPATH/elements-cli -datadir=$HOME/elementsdir1"
alias e1-dae="$ELEMENTSPATH/elementsd -datadir=$HOME/elementsdir1"
alias e2-cli="$ELEMENTSPATH/elements-cli -datadir=$HOME/elementsdir2"
alias e2-dae="$ELEMENTSPATH/elementsd -datadir=$HOME/elementsdir2"
alias b-cli="$BITCOINPATH/bitcoin-cli -datadir=$HOME/bitcoindir"
alias b-dae="$BITCOINPATH/bitcoind -datadir=$HOME/bitcoindir"
# Should throw an error, can't connect to bitcoin daemon to validate pegins
e1-dae
# Need to start bitcoind first, elementsd will wait until bitcoind gives warmup finished status
b-dae
e1-dae
e2-dae
# Alternatively, you can set validatepegin=0 in their configs and don't
# run the bitcoin node, but it is necessary for the two way peg parts of
# this tutorial.
# We have 21M OP_TRUE value in each wallet
# This is useful for testing and non-sidechain applications of Elements
e1-cli getwalletinfo
e2-cli getwalletinfo
######## WALLET ###########
#Sample raw transaction RPC API
# ~Core API
#* `getrawtransaction <txid> 1`
#* `gettransaction <txid> 1`
#* `listunspent`
#* `decoderawtransaction <hex>`
#* `sendrawtransaction <hex>`
#* `validateaddress <address>
#* `listreceivedbyaddress <minconf> <include_empty> <include_watchonly>`
# Elements Only API
#* `blindrawtransaction <hex>`
#* `dumpblindingkey <address>`
#* `importblindingkey <addr> <blindingkey>`
# But let's start with a managed wallet example
# First, drain OP_TRUE and split funds evenly between e1 and e2
e1-cli sendtoaddress $(e1-cli getnewaddress) 21000000 "" "" true
e1-cli generate 101
e1-cli sendtoaddress $(e2-cli getnewaddress) 10500000 "" "" false
e1-cli generate 101
# Funds should be evenly split
e1-cli getwalletinfo
e2-cli getwalletinfo
# Have Bob send coins to himself using a blinded Elements address!
# Blinded addresses start with `CTE`, unblinded `2`
ADDR=$(e2-cli getnewaddress)
# How do we know it's blinded? Check for blinding key, unblinded address.
e2-cli validateaddress $ADDR
TXID=$(e2-cli sendtoaddress $ADDR 1)
e2-cli generate 1
# Now let's examine the transaction, both in wallet and without
# In-wallet, take a look at blinding information
e2-cli gettransaction $TXID
# e1 doesn't have in wallet
e1-cli gettransaction $TXID
# public info, see blinded ranges, etc
e1-cli getrawtransaction $TXID 1
# Now let's import the key to spend
e1-cli importprivkey $(e2-cli dumpprivkey $ADDR)
# We can't see output value info though
e1-cli gettransaction $TXID
# And it won't show in balance or known outputs
e1-cli getwalletinfo
# Amount for transaction is unknown, so it is not shown.
e1-cli listunspent 1 1
# Solution: Import blinding key
e1-cli importblindingkey $ADDR $(e2-cli dumpblindingkey $ADDR)
# Check again, funds should show
e1-cli getwalletinfo
e1-cli listunspent 1 1
e1-cli gettransaction $TXID
#Exercises
#===
# Resources: https://bitcoin.org/en/developer-documentation
#1. Find the change output in one of your transactions.
#2. Use both methods to get the total input value of the transaction.
#3. Find your UTXO with the most confirmations.
#4. Create a raw transaction that pays 0.1 coins in fees and has two change addresses.
#5. Build blinded multisig p2sh
###### ASSETS #######
# Many of the RPC calls have added asset type or label
# arguments and reveal alternative asset information. With no argument all are listed:
e1-cli getwalletinfo
# Notice we now see "bitcoin" as an asset. This is the asset label for the hex for "bitcoin" which can be discovered:
e1-cli dumpassetlabels
# You can also filter calls using specific asset hex or labels:
e1-cli getwalletinfo bitcoin
# bitcoin's hex asset type
e1-cli getwalletinfo b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23
# We can also issue our own assets, 1 asset and 1 reissuance token in this case
ISSUE=$(e1-cli issueasset 1 1)
echo $ISSUE
ASSET=$(echo $ISSUE | jq '.asset' | tr -d '"')
echo $ASSET
# From there you can look at the issuances you have in your wallet
e1-cli listissuances
# If you gave `issueasset` a 2nd argument greater than 0, you can also reissue the base asset
e1-cli reissueasset $ASSET 1
# or make another unblinded asset issuance, with only reissuance tokens initially
e1-cli issueasset 0 1 false
# Then two issuances for that particular asset will show
e1-cli listissuances $ASSET
# To label the asset add this to your elements.conf file then restart your daemon:
# assetdir=$ASSET:yourlabelhere
# It really doesn't matter what you call it, labels are local things only.
# To send issued assets, add an additional argument to sendtoaddress using the hex or label
e1-cli sendtoaddress $(e2-cli getnewaddress) 1 "" "" false $ASSET
e2-cli getwalletinfo $ASSET
e2-cli generate 1
# e2 doesn't know about the issuance for the transaction sending him the new asset
e2-cli listissuances
# let's import an associated address and rescan
TXID=$(echo $ISSUE | jq '.txid' | tr -d '"')
ADDR=$(e1-cli gettransaction $TXID | jq '.details[0].address' | tr -d '"')
e2-cli importaddress $ADDR
# e2 now sees issuance, but doesn't know amounts as they are blinded
e2-cli listissuances
# We need to import the issuance blinding key. We refer to issuances by their txid/vin pair
# as there is only one per input
VIN=$(echo $ISSUE | jq '.vin' | tr -d '"')
ISSUEKEY=$(e1-cli dumpissuanceblindingkey $TXID $VIN)
echo $ISSUEKEY
e2-cli importissuanceblindingkey $TXID $VIN $ISSUEKEY
# Now e2 can see issuance amounts and blinds
e2-cli listissuances
###### BLOCKSIGNING #######
# Recall blocksigning is OP_TRUE
e1-cli generate 1
# Let's set it to something more interesting... 2-of-2 multisig
# First lets get some keys from both clients to make our block "challenge"
ADDR1=$(e1-cli getnewaddress)
ADDR2=$(e2-cli getnewaddress)
VALID1=$(e1-cli validateaddress $ADDR1)
PUBKEY1=$(echo $VALID1 | python3 -c "import sys, json; print(json.load(sys.stdin)['pubkey'])")
VALID2=$(e2-cli validateaddress $ADDR2)
PUBKEY2=$(echo $VALID2 | python3 -c "import sys, json; print(json.load(sys.stdin)['pubkey'])")
KEY1=$(e1-cli dumpprivkey $ADDR1)
KEY2=$(e2-cli dumpprivkey $ADDR2)
e1-cli stop
e2-cli stop
# Now filled with the pubkeys as 2-of-2 checkmultisig
SIGNBLOCKARG="-signblockscript=5221$(echo $PUBKEY1)21$(echo $PUBKEY2)52ae"
# Wipe out the chain and wallet to get funds with new genesis block
# You can not swap out blocksigner sets as of now for security reasons,
# so we start fresh on a new chain.
rm -r ~/elementsdir1/elementsregtest/blocks
rm -r ~/elementsdir1/elementsregtest/chainstate
rm ~/elementsdir1/elementsregtest/wallet.dat
rm -r ~/elementsdir2/elementsregtest/blocks
rm -r ~/elementsdir2/elementsregtest/chainstate
rm ~/elementsdir2/elementsregtest/wallet.dat
e1-dae $SIGNBLOCKARG
e2-dae $SIGNBLOCKARG
# Now import signing keys
e1-cli importprivkey $KEY1
e2-cli importprivkey $KEY2
# Generate no longer works, even if keys are in wallet
e1-cli generate 1
e2-cli generate 1
# Let's propose and accept some blocks, e1 is master!
HEX=$(e1-cli getnewblockhex)
# Unsigned is no good
# 0 before, 0 after
e1-cli getblockcount
e1-cli submitblock $HEX
# Still 0
e1-cli getblockcount
####
# Signblock tests validity except block signatures
# This signing step can be outsourced to a HSM signing to enforce business logic of any sort
# See Strong Federations paper
SIGN1=$(e1-cli signblock $HEX)
SIGN2=$(e2-cli signblock $HEX)
####
# We now can gather signatures any way you want, combine them into a fully signed block
BLOCKRESULT=$(e1-cli combineblocksigs $HEX '''["'''$SIGN1'''", "'''$SIGN2'''"]''')
COMPLETE=$(echo $BLOCKRESULT | python3 -c "import sys, json; print(json.load(sys.stdin)['complete'])")
SIGNBLOCK=$(echo $BLOCKRESULT | python3 -c "import sys, json; print(json.load(sys.stdin)['hex'])")
# Should get True here as we have signatures from each key
echo $COMPLETE
# Now submit the block, doesn't matter who
e2-cli submitblock $SIGNBLOCK
# We now have moved forward one block!
e1-cli getblockcount
e2-cli getblockcount
e1-cli stop
e2-cli stop
# Further Exercises:
# 1.Make funny/different block block challenge? modify generate to allow arbitrary proof, instead of from wallet only
# 2.Arbitrary consensus change?
# 3.Make a python script that does round-robin consensus
######## Pegging #######
# Everything pegging related can be done inside the Elements daemon directly, except for
# pegging out. This is due to the multisig pool aka Watchmen that controls the bitcoin
# on the Bitcoin blockchain. That is the easiest part to get wrong, and by far the most
# important as there is no going back if you lose the funds.
# Wipe out the chain and wallet to get funds with new genesis block
rm -r ~/elementsdir1/elementsregtest/blocks
rm -r ~/elementsdir1/elementsregtest/chainstate
rm ~/elementsdir1/elementsregtest/wallet.dat
rm -r ~/elementsdir2/elementsregtest/blocks
rm -r ~/elementsdir2/elementsregtest/chainstate
rm ~/elementsdir2/elementsregtest/wallet.dat
FEDPEGARG="-fedpegscript=5221$(echo $PUBKEY1)21$(echo $PUBKEY2)52ae"
# Back to OP_TRUE blocks, re-using pubkeys for pegin pool instead
# Keys can be the same or different, doesn't matter
e1-dae $FEDPEGARG
e2-dae $FEDPEGARG
# Mature some outputs on each side
e1-cli generate 101
b-cli generate 101
# Now we can actually start pegging in. Examine the pegin address fields
e1-cli getpeginaddress
# Changes each time as it's a new sidechain address as well as new "tweak" for the watchmen keys
# mainchain_address : where you send your bitcoin from Bitcoin network
# claim_script: what script will have to be satisfied to spent the peg-in input
# Each call of this takes the pubkeys defined in the config file, adds a random number to them
# that is essetially the hash of the sidechain_address and other information,
# then creates a new P2SH Bitcoin address from that. We reveal that "tweak" to the functionaries
# during `claimpegin`, then they are able to calculate the necessary private key and control
# funds.
e1-cli getpeginaddress
ADDRS=$(e1-cli getpeginaddress)
MAINCHAIN=$(echo $ADDRS | python3 -c "import sys, json; print(json.load(sys.stdin)['mainchain_address'])")
SIDECHAIN=$(echo $ADDRS | python3 -c "import sys, json; print(json.load(sys.stdin)['claim_script'])")
#Send funds to unique watchmen P2SH address
TXID=$(b-cli sendtoaddress $MAINCHAIN 1)
# Mature pegin funds to avoid reorg -> fractional reserve
b-cli generate 101
PROOF=$(b-cli gettxoutproof '''["'''$TXID'''"]''')
RAW=$(b-cli getrawtransaction $TXID)
# Attempt claim!
CLAIMTXID=$(e1-cli claimpegin $RAW $PROOF)
# Other node should accept to mempool and mine
e2-cli generate 1
# Should see confirmations
e1-cli getrawtransaction $CLAIMTXID 1
#### Pegging Out ####
e1-cli sendtomainchain $(b-cli getnewaddress) 10
#Exercises
#1. Implement really dumb/unsafe watchmen to allow pegouts for learning purposes
# Recover tweak from pegin, add to privkey, combined tweaked pubkeys into a redeemscript, add to Core wallet