6
6
7
7
from decimal import Decimal
8
8
from test_framework .address import ADDRESS_BCRT1_P2WSH_OP_TRUE
9
+ from test_framework .key import ECKey
9
10
from test_framework .messages import (
10
11
COIN ,
11
12
COutPoint ,
16
17
)
17
18
from test_framework .script import (
18
19
CScript ,
20
+ LegacySignatureHash ,
21
+ OP_CHECKSIG ,
19
22
OP_TRUE ,
20
23
OP_NOP ,
24
+ SIGHASH_ALL ,
21
25
)
22
26
from test_framework .util import (
23
27
assert_equal ,
27
31
28
32
29
33
class MiniWallet :
30
- def __init__ (self , test_node , * , raw_script = False ):
34
+ def __init__ (self , test_node , * , raw_script = False , use_p2pk = False ):
31
35
self ._test_node = test_node
32
36
self ._utxos = []
37
+ self ._priv_key = None
38
+ self ._address = None
39
+
33
40
if raw_script :
34
- self ._address = None
35
41
self ._scriptPubKey = bytes (CScript ([OP_TRUE ]))
42
+ elif use_p2pk :
43
+ # use simple deterministic private key (k=1)
44
+ self ._priv_key = ECKey ()
45
+ self ._priv_key .set ((1 ).to_bytes (32 , 'big' ), True )
46
+ pub_key = self ._priv_key .get_pubkey ()
47
+ self ._scriptPubKey = bytes (CScript ([pub_key .get_bytes (), OP_CHECKSIG ]))
36
48
else :
37
49
self ._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
38
50
self ._scriptPubKey = hex_str_to_bytes (self ._test_node .validateaddress (self ._address )['scriptPubKey' ])
@@ -50,6 +62,13 @@ def scan_tx(self, tx):
50
62
if out ['scriptPubKey' ]['hex' ] == self ._scriptPubKey .hex ():
51
63
self ._utxos .append ({'txid' : tx ['txid' ], 'vout' : out ['n' ], 'value' : out ['value' ]})
52
64
65
+ def sign_tx (self , tx ):
66
+ """Sign tx that has been created by MiniWallet in P2PK mode"""
67
+ assert self ._priv_key is not None
68
+ (sighash , err ) = LegacySignatureHash (CScript (self ._scriptPubKey ), tx , 0 , SIGHASH_ALL )
69
+ assert err is None
70
+ tx .vin [0 ].scriptSig = CScript ([self ._priv_key .sign_ecdsa (sighash ) + bytes (bytearray ([SIGHASH_ALL ]))])
71
+
53
72
def generate (self , num_blocks ):
54
73
"""Generate blocks with coinbase outputs to the internal address, and append the outputs to the internal list"""
55
74
blocks = self ._test_node .generatetodescriptor (num_blocks , f'raw({ self ._scriptPubKey .hex ()} )' )
@@ -99,7 +118,12 @@ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_
99
118
tx .vout = [CTxOut (int (send_value * COIN ), self ._scriptPubKey )]
100
119
if not self ._address :
101
120
# raw script
102
- tx .vin [0 ].scriptSig = CScript ([OP_NOP ] * 35 ) # pad to identical size
121
+ if self ._priv_key is not None :
122
+ # P2PK, need to sign
123
+ self .sign_tx (tx )
124
+ else :
125
+ # anyone-can-spend
126
+ tx .vin [0 ].scriptSig = CScript ([OP_NOP ] * 35 ) # pad to identical size
103
127
else :
104
128
tx .wit .vtxinwit = [CTxInWitness ()]
105
129
tx .wit .vtxinwit [0 ].scriptWitness .stack = [CScript ([OP_TRUE ])]
@@ -108,7 +132,10 @@ def create_self_transfer(self, *, fee_rate=Decimal("0.003"), from_node, utxo_to_
108
132
tx_info = from_node .testmempoolaccept ([tx_hex ])[0 ]
109
133
assert_equal (mempool_valid , tx_info ['allowed' ])
110
134
if mempool_valid :
111
- assert_equal (tx_info ['vsize' ], vsize )
135
+ # TODO: for P2PK, vsize is not constant due to varying scriptSig length,
136
+ # so only check this for anyone-can-spend outputs right now
137
+ if self ._priv_key is None :
138
+ assert_equal (tx_info ['vsize' ], vsize )
112
139
assert_equal (tx_info ['fees' ]['base' ], fee )
113
140
return {'txid' : tx_info ['txid' ], 'wtxid' : tx_info ['wtxid' ], 'hex' : tx_hex , 'tx' : tx }
114
141
0 commit comments