22
22
)
23
23
from test_framework .script import (
24
24
ANNEX_TAG ,
25
+ BIP341_sha_amounts ,
26
+ BIP341_sha_outputs ,
27
+ BIP341_sha_prevouts ,
28
+ BIP341_sha_scriptpubkeys ,
29
+ BIP341_sha_sequences ,
25
30
CScript ,
26
31
CScriptNum ,
27
32
CScriptOp ,
79
84
)
80
85
from test_framework .script_util import (
81
86
key_to_p2pk_script ,
87
+ key_to_p2pkh_script ,
82
88
key_to_p2wpkh_script ,
83
89
keyhash_to_p2pkh_script ,
84
90
script_to_p2sh_script ,
89
95
from test_framework .key import generate_privkey , compute_xonly_pubkey , sign_schnorr , tweak_add_privkey , ECKey
90
96
from test_framework .address import (
91
97
hash160 ,
98
+ program_to_witness
92
99
)
93
100
from collections import OrderedDict , namedtuple
94
101
from io import BytesIO
97
104
import os
98
105
import random
99
106
107
+ # Whether or not to output generated test vectors, in JSON format.
108
+ GEN_TEST_VECTORS = False
109
+
100
110
# === Framework for building spending transactions. ===
101
111
#
102
112
# The computation is represented as a "context" dict, whose entries store potentially-unevaluated expressions that
@@ -418,6 +428,7 @@ def flatten(lst):
418
428
ret .append (elem )
419
429
return ret
420
430
431
+
421
432
def spend (tx , idx , utxos , ** kwargs ):
422
433
"""Sign transaction input idx of tx, provided utxos is the list of outputs being spent.
423
434
@@ -1276,6 +1287,14 @@ def block_submit(self, node, txs, msg, err_msg, cb_pubkey=None, fees=0, sigops_w
1276
1287
else :
1277
1288
assert node .getbestblockhash () == self .lastblockhash , "Failed to reject: " + msg
1278
1289
1290
+ def init_blockinfo (self , node ):
1291
+ # Initialize variables used by block_submit().
1292
+ self .lastblockhash = node .getbestblockhash ()
1293
+ self .tip = int (self .lastblockhash , 16 )
1294
+ block = node .getblock (self .lastblockhash )
1295
+ self .lastblockheight = block ['height' ]
1296
+ self .lastblocktime = block ['time' ]
1297
+
1279
1298
def test_spenders (self , node , spenders , input_counts ):
1280
1299
"""Run randomized tests with a number of "spenders".
1281
1300
@@ -1302,12 +1321,7 @@ def test_spenders(self, node, spenders, input_counts):
1302
1321
host_spks .append (spk )
1303
1322
host_pubkeys .append (bytes .fromhex (info ['pubkey' ]))
1304
1323
1305
- # Initialize variables used by block_submit().
1306
- self .lastblockhash = node .getbestblockhash ()
1307
- self .tip = int (self .lastblockhash , 16 )
1308
- block = node .getblock (self .lastblockhash )
1309
- self .lastblockheight = block ['height' ]
1310
- self .lastblocktime = block ['time' ]
1324
+ self .init_blockinfo (node )
1311
1325
1312
1326
# Create transactions spending up to 50 of the wallet's inputs, with one output for each spender, and
1313
1327
# one change output at the end. The transaction is constructed on the Python side to enable
@@ -1481,10 +1495,239 @@ def test_spenders(self, node, spenders, input_counts):
1481
1495
assert len (mismatching_utxos ) == 0
1482
1496
self .log .info (" - Done" )
1483
1497
1498
+ def gen_test_vectors (self ):
1499
+ """Run a scenario that corresponds (and optionally produces) to BIP341 test vectors."""
1500
+
1501
+ self .log .info ("Unit test scenario..." )
1502
+
1503
+ # Deterministically mine coins to OP_TRUE in block 1
1504
+ assert self .nodes [1 ].getblockcount () == 0
1505
+ coinbase = CTransaction ()
1506
+ coinbase .nVersion = 1
1507
+ coinbase .vin = [CTxIn (COutPoint (0 , 0xffffffff ), CScript ([OP_1 , OP_1 ]), 0xffffffff )]
1508
+ coinbase .vout = [CTxOut (5000000000 , CScript ([OP_1 ]))]
1509
+ coinbase .nLockTime = 0
1510
+ coinbase .rehash ()
1511
+ assert coinbase .hash == "f60c73405d499a956d3162e3483c395526ef78286458a4cb17b125aa92e49b20"
1512
+ # Mine it
1513
+ block = create_block (hashprev = int (self .nodes [1 ].getbestblockhash (), 16 ), coinbase = coinbase )
1514
+ block .rehash ()
1515
+ block .solve ()
1516
+ self .nodes [1 ].submitblock (block .serialize ().hex ())
1517
+ assert self .nodes [1 ].getblockcount () == 1
1518
+ self .generate (self .nodes [1 ], COINBASE_MATURITY )
1519
+
1520
+ SEED = 317
1521
+ VALID_LEAF_VERS = list (range (0xc0 , 0x100 , 2 )) + [0x66 , 0x7e , 0x80 , 0x84 , 0x96 , 0x98 , 0xba , 0xbc , 0xbe ]
1522
+ # Generate private keys
1523
+ prvs = [hashlib .sha256 (SEED .to_bytes (2 , 'big' ) + bytes ([i ])).digest () for i in range (100 )]
1524
+ # Generate corresponding public x-only pubkeys
1525
+ pubs = [compute_xonly_pubkey (prv )[0 ] for prv in prvs ]
1526
+ # Generate taproot objects
1527
+ inner_keys = [pubs [i ] for i in range (7 )]
1528
+
1529
+ script_lists = [
1530
+ None ,
1531
+ [("0" , CScript ([pubs [50 ], OP_CHECKSIG ]), 0xc0 )],
1532
+ [("0" , CScript ([pubs [51 ], OP_CHECKSIG ]), 0xc0 )],
1533
+ [("0" , CScript ([pubs [52 ], OP_CHECKSIG ]), 0xc0 ), ("1" , CScript ([b"BIP341" ]), VALID_LEAF_VERS [pubs [99 ][0 ] % 41 ])],
1534
+ [("0" , CScript ([pubs [53 ], OP_CHECKSIG ]), 0xc0 ), ("1" , CScript ([b"Taproot" ]), VALID_LEAF_VERS [pubs [99 ][1 ] % 41 ])],
1535
+ [("0" , CScript ([pubs [54 ], OP_CHECKSIG ]), 0xc0 ), [("1" , CScript ([pubs [55 ], OP_CHECKSIG ]), 0xc0 ), ("2" , CScript ([pubs [56 ], OP_CHECKSIG ]), 0xc0 )]],
1536
+ [("0" , CScript ([pubs [57 ], OP_CHECKSIG ]), 0xc0 ), [("1" , CScript ([pubs [58 ], OP_CHECKSIG ]), 0xc0 ), ("2" , CScript ([pubs [59 ], OP_CHECKSIG ]), 0xc0 )]],
1537
+ ]
1538
+ taps = [taproot_construct (inner_keys [i ], script_lists [i ]) for i in range (len (inner_keys ))]
1539
+
1540
+ # Require negated taps[0]
1541
+ assert taps [0 ].negflag
1542
+ # Require one negated and one non-negated in taps 1 and 2.
1543
+ assert taps [1 ].negflag != taps [2 ].negflag
1544
+ # Require one negated and one non-negated in taps 3 and 4.
1545
+ assert taps [3 ].negflag != taps [4 ].negflag
1546
+ # Require one negated and one non-negated in taps 5 and 6.
1547
+ assert taps [5 ].negflag != taps [6 ].negflag
1548
+
1549
+ cblks = [{leaf : get ({** DEFAULT_CONTEXT , 'tap' : taps [i ], 'leaf' : leaf }, 'controlblock' ) for leaf in taps [i ].leaves } for i in range (7 )]
1550
+ # Require one swapped and one unswapped in taps 3 and 4.
1551
+ assert (cblks [3 ]['0' ][33 :65 ] < cblks [3 ]['1' ][33 :65 ]) != (cblks [4 ]['0' ][33 :65 ] < cblks [4 ]['1' ][33 :65 ])
1552
+ # Require one swapped and one unswapped in taps 5 and 6, both at the top and child level.
1553
+ assert (cblks [5 ]['0' ][33 :65 ] < cblks [5 ]['1' ][65 :]) != (cblks [6 ]['0' ][33 :65 ] < cblks [6 ]['1' ][65 :])
1554
+ assert (cblks [5 ]['1' ][33 :65 ] < cblks [5 ]['2' ][33 :65 ]) != (cblks [6 ]['1' ][33 :65 ] < cblks [6 ]['2' ][33 :65 ])
1555
+ # Require within taps 5 (and thus also 6) that one level is swapped and the other is not.
1556
+ assert (cblks [5 ]['0' ][33 :65 ] < cblks [5 ]['1' ][65 :]) != (cblks [5 ]['1' ][33 :65 ] < cblks [5 ]['2' ][33 :65 ])
1557
+
1558
+ # Compute a deterministic set of scriptPubKeys
1559
+ tap_spks = []
1560
+ old_spks = []
1561
+ spend_info = {}
1562
+ # First, taproot scriptPubKeys, for the tap objects constructed above
1563
+ for i , tap in enumerate (taps ):
1564
+ tap_spks .append (tap .scriptPubKey )
1565
+ d = {'key' : prvs [i ], 'tap' : tap , 'mode' : 'taproot' }
1566
+ spend_info [tap .scriptPubKey ] = d
1567
+ # Then, a number of deterministically generated (keys 0x1,0x2,0x3) with 2x P2PKH, 1x P2WPKH spks.
1568
+ for i in range (1 , 4 ):
1569
+ prv = ECKey ()
1570
+ prv .set (i .to_bytes (32 , 'big' ), True )
1571
+ pub = prv .get_pubkey ().get_bytes ()
1572
+ d = {"key" : prv }
1573
+ d ["scriptcode" ] = key_to_p2pkh_script (pub )
1574
+ d ["inputs" ] = [getter ("sign" ), pub ]
1575
+ if i < 3 :
1576
+ # P2PKH
1577
+ d ['spk' ] = key_to_p2pkh_script (pub )
1578
+ d ['mode' ] = 'legacy'
1579
+ else :
1580
+ # P2WPKH
1581
+ d ['spk' ] = key_to_p2wpkh_script (pub )
1582
+ d ['mode' ] = 'witv0'
1583
+ old_spks .append (d ['spk' ])
1584
+ spend_info [d ['spk' ]] = d
1585
+
1586
+ # Construct a deterministic chain of transactions creating UTXOs to the test's spk's (so that they
1587
+ # come from distinct txids).
1588
+ txn = []
1589
+ lasttxid = coinbase .sha256
1590
+ amount = 5000000000
1591
+ for i , spk in enumerate (old_spks + tap_spks ):
1592
+ val = 42000000 * (i + 7 )
1593
+ tx = CTransaction ()
1594
+ tx .nVersion = 1
1595
+ tx .vin = [CTxIn (COutPoint (lasttxid , i & 1 ), CScript ([]), 0xffffffff )]
1596
+ tx .vout = [CTxOut (val , spk ), CTxOut (amount - val , CScript ([OP_1 ]))]
1597
+ if i & 1 :
1598
+ tx .vout = list (reversed (tx .vout ))
1599
+ tx .nLockTime = 0
1600
+ tx .rehash ()
1601
+ amount -= val
1602
+ lasttxid = tx .sha256
1603
+ txn .append (tx )
1604
+ spend_info [spk ]['prevout' ] = COutPoint (tx .sha256 , i & 1 )
1605
+ spend_info [spk ]['utxo' ] = CTxOut (val , spk )
1606
+ # Mine those transactions
1607
+ self .init_blockinfo (self .nodes [1 ])
1608
+ self .block_submit (self .nodes [1 ], txn , "Crediting txn" , None , sigops_weight = 10 , accept = True )
1609
+
1610
+ # scriptPubKey computation
1611
+ tests = {"version" : 1 }
1612
+ spk_tests = tests .setdefault ("scriptPubKey" , [])
1613
+ for i , tap in enumerate (taps ):
1614
+ test_case = {}
1615
+ given = test_case .setdefault ("given" , {})
1616
+ given ['internalPubkey' ] = tap .internal_pubkey .hex ()
1617
+
1618
+ def pr (node ):
1619
+ if node is None :
1620
+ return None
1621
+ elif isinstance (node , tuple ):
1622
+ return {"id" : int (node [0 ]), "script" : node [1 ].hex (), "leafVersion" : node [2 ]}
1623
+ elif len (node ) == 1 :
1624
+ return pr (node [0 ])
1625
+ elif len (node ) == 2 :
1626
+ return [pr (node [0 ]), pr (node [1 ])]
1627
+ else :
1628
+ assert False
1629
+
1630
+ given ['scriptTree' ] = pr (script_lists [i ])
1631
+ intermediary = test_case .setdefault ("intermediary" , {})
1632
+ if len (tap .leaves ):
1633
+ leafhashes = intermediary .setdefault ('leafHashes' , [None ] * len (tap .leaves ))
1634
+ for leaf in tap .leaves :
1635
+ leafhashes [int (leaf )] = tap .leaves [leaf ].leaf_hash .hex ()
1636
+ intermediary ['merkleRoot' ] = tap .merkle_root .hex () if tap .merkle_root else None
1637
+ intermediary ['tweak' ] = tap .tweak .hex ()
1638
+ intermediary ['tweakedPubkey' ] = tap .output_pubkey .hex ()
1639
+ expected = test_case .setdefault ("expected" , {})
1640
+ expected ['scriptPubKey' ] = tap .scriptPubKey .hex ()
1641
+ expected ['bip350Address' ] = program_to_witness (1 , bytes (tap .output_pubkey ), True )
1642
+ if len (tap .leaves ):
1643
+ control_blocks = expected .setdefault ("scriptPathControlBlocks" , [None ] * len (tap .leaves ))
1644
+ for leaf in tap .leaves :
1645
+ ctx = {** DEFAULT_CONTEXT , 'tap' : tap , 'leaf' : leaf }
1646
+ control_blocks [int (leaf )] = get (ctx , "controlblock" ).hex ()
1647
+ spk_tests .append (test_case )
1648
+
1649
+ # Construct a deterministic transaction spending all outputs created above.
1650
+ tx = CTransaction ()
1651
+ tx .nVersion = 2
1652
+ tx .vin = []
1653
+ inputs = []
1654
+ input_spks = [tap_spks [0 ], tap_spks [1 ], old_spks [0 ], tap_spks [2 ], tap_spks [5 ], old_spks [2 ], tap_spks [6 ], tap_spks [3 ], tap_spks [4 ]]
1655
+ sequences = [0 , 0xffffffff , 0xffffffff , 0xfffffffe , 0xfffffffe , 0 , 0 , 0xffffffff , 0xffffffff ]
1656
+ hashtypes = [SIGHASH_SINGLE , SIGHASH_SINGLE | SIGHASH_ANYONECANPAY , SIGHASH_ALL , SIGHASH_ALL , SIGHASH_DEFAULT , SIGHASH_ALL , SIGHASH_NONE , SIGHASH_NONE | SIGHASH_ANYONECANPAY , SIGHASH_ALL | SIGHASH_ANYONECANPAY ]
1657
+ for i , spk in enumerate (input_spks ):
1658
+ tx .vin .append (CTxIn (spend_info [spk ]['prevout' ], CScript (), sequences [i ]))
1659
+ inputs .append (spend_info [spk ]['utxo' ])
1660
+ tx .vout .append (CTxOut (1000000000 , old_spks [1 ]))
1661
+ tx .vout .append (CTxOut (3410000000 , pubs [98 ]))
1662
+ tx .nLockTime = 500000000
1663
+ precomputed = {
1664
+ "hashAmounts" : BIP341_sha_amounts (inputs ),
1665
+ "hashPrevouts" : BIP341_sha_prevouts (tx ),
1666
+ "hashScriptPubkeys" : BIP341_sha_scriptpubkeys (inputs ),
1667
+ "hashSequences" : BIP341_sha_sequences (tx ),
1668
+ "hashOutputs" : BIP341_sha_outputs (tx )
1669
+ }
1670
+ keypath_tests = tests .setdefault ("keyPathSpending" , [])
1671
+ tx_test = {}
1672
+ global_given = tx_test .setdefault ("given" , {})
1673
+ global_given ['rawUnsignedTx' ] = tx .serialize ().hex ()
1674
+ utxos_spent = global_given .setdefault ("utxosSpent" , [])
1675
+ for i in range (len (input_spks )):
1676
+ utxos_spent .append ({"scriptPubKey" : inputs [i ].scriptPubKey .hex (), "amountSats" : inputs [i ].nValue })
1677
+ global_intermediary = tx_test .setdefault ("intermediary" , {})
1678
+ for key in sorted (precomputed .keys ()):
1679
+ global_intermediary [key ] = precomputed [key ].hex ()
1680
+ test_list = tx_test .setdefault ('inputSpending' , [])
1681
+ for i in range (len (input_spks )):
1682
+ ctx = {
1683
+ ** DEFAULT_CONTEXT ,
1684
+ ** spend_info [input_spks [i ]],
1685
+ 'tx' : tx ,
1686
+ 'utxos' : inputs ,
1687
+ 'idx' : i ,
1688
+ 'hashtype' : hashtypes [i ],
1689
+ 'deterministic' : True
1690
+ }
1691
+ if ctx ['mode' ] == 'taproot' :
1692
+ test_case = {}
1693
+ given = test_case .setdefault ("given" , {})
1694
+ given ['txinIndex' ] = i
1695
+ given ['internalPrivkey' ] = get (ctx , 'key' ).hex ()
1696
+ if get (ctx , "tap" ).merkle_root != bytes ():
1697
+ given ['merkleRoot' ] = get (ctx , "tap" ).merkle_root .hex ()
1698
+ else :
1699
+ given ['merkleRoot' ] = None
1700
+ given ['hashType' ] = get (ctx , "hashtype" )
1701
+ intermediary = test_case .setdefault ("intermediary" , {})
1702
+ intermediary ['internalPubkey' ] = get (ctx , "tap" ).internal_pubkey .hex ()
1703
+ intermediary ['tweak' ] = get (ctx , "tap" ).tweak .hex ()
1704
+ intermediary ['tweakedPrivkey' ] = get (ctx , "key_tweaked" ).hex ()
1705
+ sigmsg = get (ctx , "sigmsg" )
1706
+ intermediary ['sigMsg' ] = sigmsg .hex ()
1707
+ intermediary ['precomputedUsed' ] = [key for key in sorted (precomputed .keys ()) if sigmsg .count (precomputed [key ])]
1708
+ intermediary ['sigHash' ] = get (ctx , "sighash" ).hex ()
1709
+ expected = test_case .setdefault ("expected" , {})
1710
+ expected ['witness' ] = [get (ctx , "sign" ).hex ()]
1711
+ test_list .append (test_case )
1712
+ tx .wit .vtxinwit .append (CTxInWitness ())
1713
+ tx .vin [i ].scriptSig = CScript (flatten (get (ctx , "scriptsig" )))
1714
+ tx .wit .vtxinwit [i ].scriptWitness .stack = flatten (get (ctx , "witness" ))
1715
+ aux = tx_test .setdefault ("auxiliary" , {})
1716
+ aux ['fullySignedTx' ] = tx .serialize ().hex ()
1717
+ keypath_tests .append (tx_test )
1718
+ assert_equal (hashlib .sha256 (tx .serialize ()).hexdigest (), "24bab662cb55a7f3bae29b559f651674c62bcc1cd442d44715c0133939107b38" )
1719
+ # Mine the spending transaction
1720
+ self .block_submit (self .nodes [1 ], [tx ], "Spending txn" , None , sigops_weight = 10000 , accept = True , witness = True )
1721
+
1722
+ if GEN_TEST_VECTORS :
1723
+ print (json .dumps (tests , indent = 4 , sort_keys = False ))
1724
+
1725
+
1484
1726
def run_test (self ):
1727
+ self .gen_test_vectors ()
1728
+
1485
1729
# Post-taproot activation tests go first (pre-taproot tests' blocks are invalid post-taproot).
1486
1730
self .log .info ("Post-activation tests..." )
1487
- self .generate (self .nodes [1 ], COINBASE_MATURITY + 1 )
1488
1731
self .test_spenders (self .nodes [1 ], spenders_taproot_active (), input_counts = [1 , 2 , 2 , 2 , 2 , 3 ])
1489
1732
1490
1733
# Re-connect nodes in case they have been disconnected
0 commit comments