@@ -18,7 +18,7 @@ PATH_BASE_CONTRIB_SIGNET = os.path.abspath(os.path.dirname(os.path.realpath(__fi
18
18
PATH_BASE_TEST_FUNCTIONAL = os .path .abspath (os .path .join (PATH_BASE_CONTRIB_SIGNET , ".." , ".." , "test" , "functional" ))
19
19
sys .path .insert (0 , PATH_BASE_TEST_FUNCTIONAL )
20
20
21
- from test_framework .blocktools import get_witness_script , script_BIP34_coinbase_height # noqa: E402
21
+ from test_framework .blocktools import get_witness_script , script_BIP34_coinbase_height , SIGNET_HEADER # noqa: E402
22
22
from test_framework .messages import CBlock , CBlockHeader , COutPoint , CTransaction , CTxIn , CTxInWitness , CTxOut , from_binary , from_hex , ser_string , ser_uint256 , tx_from_hex , MAX_SEQUENCE_NONFINAL # noqa: E402
23
23
from test_framework .psbt import PSBT , PSBTMap , PSBT_GLOBAL_UNSIGNED_TX , PSBT_IN_FINAL_SCRIPTSIG , PSBT_IN_FINAL_SCRIPTWITNESS , PSBT_IN_NON_WITNESS_UTXO , PSBT_IN_SIGHASH_TYPE # noqa: E402
24
24
from test_framework .script import CScript , CScriptOp # noqa: E402
@@ -28,7 +28,6 @@ logging.basicConfig(
28
28
level = logging .INFO ,
29
29
datefmt = '%Y-%m-%d %H:%M:%S' )
30
30
31
- SIGNET_HEADER = b"\xec \xc7 \xda \xa2 "
32
31
PSBT_SIGNET_BLOCK = b"\xfc \x06 signetb" # proprietary PSBT global field holding the block being signed
33
32
RE_MULTIMINER = re .compile (r"^(\d+)(-(\d+))?/(\d+)$" )
34
33
@@ -66,22 +65,31 @@ def signet_txs(block, challenge):
66
65
67
66
return spend , to_spend
68
67
69
- def decode_psbt (b64psbt ):
68
+ def decode_challenge_psbt (b64psbt ):
70
69
psbt = PSBT .from_base64 (b64psbt )
71
70
72
71
assert len (psbt .tx .vin ) == 1
73
72
assert len (psbt .tx .vout ) == 1
74
73
assert PSBT_SIGNET_BLOCK in psbt .g .map
74
+ return psbt
75
75
76
+ def get_block_from_psbt (psbt ):
77
+ return from_binary (CBlock , psbt .g .map [PSBT_SIGNET_BLOCK ])
78
+
79
+ def get_solution_from_psbt (psbt , emptyok = False ):
76
80
scriptSig = psbt .i [0 ].map .get (PSBT_IN_FINAL_SCRIPTSIG , b"" )
77
81
scriptWitness = psbt .i [0 ].map .get (PSBT_IN_FINAL_SCRIPTWITNESS , b"\x00 " )
78
-
79
- return from_binary (CBlock , psbt .g .map [PSBT_SIGNET_BLOCK ]), ser_string (scriptSig ) + scriptWitness
82
+ if emptyok and len (scriptSig ) == 0 and scriptWitness == b"\x00 " :
83
+ return None
84
+ return ser_string (scriptSig ) + scriptWitness
80
85
81
86
def finish_block (block , signet_solution , grind_cmd ):
82
- block .vtx [0 ].vout [- 1 ].scriptPubKey += CScriptOp .encode_op_pushdata (SIGNET_HEADER + signet_solution )
83
- block .vtx [0 ].rehash ()
84
- block .hashMerkleRoot = block .calc_merkle_root ()
87
+ if signet_solution is None :
88
+ pass # Don't need to add a signet commitment if there's no signet signature needed
89
+ else :
90
+ block .vtx [0 ].vout [- 1 ].scriptPubKey += CScriptOp .encode_op_pushdata (SIGNET_HEADER + signet_solution )
91
+ block .vtx [0 ].rehash ()
92
+ block .hashMerkleRoot = block .calc_merkle_root ()
85
93
if grind_cmd is None :
86
94
block .solve ()
87
95
else :
@@ -93,10 +101,7 @@ def finish_block(block, signet_solution, grind_cmd):
93
101
block .rehash ()
94
102
return block
95
103
96
- def generate_psbt (tmpl , reward_spk , * , blocktime = None , poolid = None ):
97
- signet_spk = tmpl ["signet_challenge" ]
98
- signet_spk_bin = bytes .fromhex (signet_spk )
99
-
104
+ def new_block (tmpl , reward_spk , * , blocktime = None , poolid = None ):
100
105
scriptSig = script_BIP34_coinbase_height (tmpl ["height" ])
101
106
if poolid is not None :
102
107
scriptSig = CScript (b"" + scriptSig + CScriptOp .encode_op_pushdata (poolid ))
@@ -125,8 +130,14 @@ def generate_psbt(tmpl, reward_spk, *, blocktime=None, poolid=None):
125
130
block .vtx [0 ].wit .vtxinwit = [cbwit ]
126
131
block .vtx [0 ].vout .append (CTxOut (0 , bytes (get_witness_script (witroot , witnonce ))))
127
132
128
- signme , spendme = signet_txs (block , signet_spk_bin )
133
+ block .vtx [0 ].rehash ()
134
+ block .hashMerkleRoot = block .calc_merkle_root ()
135
+
136
+ return block
129
137
138
+ def generate_psbt (block , signet_spk ):
139
+ signet_spk_bin = bytes .fromhex (signet_spk )
140
+ signme , spendme = signet_txs (block , signet_spk_bin )
130
141
psbt = PSBT ()
131
142
psbt .g = PSBTMap ( {PSBT_GLOBAL_UNSIGNED_TX : signme .serialize (),
132
143
PSBT_SIGNET_BLOCK : block .serialize ()
@@ -175,12 +186,16 @@ def get_reward_addr_spk(args, height):
175
186
def do_genpsbt (args ):
176
187
poolid = get_poolid (args )
177
188
tmpl = json .load (sys .stdin )
189
+ signet_spk = tmpl ["signet_challenge" ]
178
190
_ , reward_spk = get_reward_addr_spk (args , tmpl ["height" ])
179
- psbt = generate_psbt (tmpl , reward_spk , poolid = poolid )
191
+ block = new_block (tmpl , reward_spk , poolid = poolid )
192
+ psbt = generate_psbt (block , signet_spk )
180
193
print (psbt )
181
194
182
195
def do_solvepsbt (args ):
183
- block , signet_solution = decode_psbt (sys .stdin .read ())
196
+ psbt = decode_challenge_psbt (sys .stdin .read ())
197
+ block = get_block_from_psbt (psbt )
198
+ signet_solution = get_solution_from_psbt (psbt , emptyok = True )
184
199
block = finish_block (block , signet_solution , args .grind_cmd )
185
200
print (block .serialize ().hex ())
186
201
@@ -223,6 +238,21 @@ def seconds_to_hms(s):
223
238
out = "-" + out
224
239
return out
225
240
241
+ def trivial_challenge (spkhex ):
242
+ """
243
+ BIP325 allows omitting the signet commitment when scriptSig and
244
+ scriptWitness are both empty. This is the case for trivial
245
+ challenges such as OP_TRUE or a single data push.
246
+ """
247
+ spk = bytes .fromhex (spkhex )
248
+ if len (spk ) == 1 and 0x51 <= spk [0 ] <= 0x60 :
249
+ # OP_TRUE/OP_1...OP_16
250
+ return True
251
+ elif 2 <= len (spk ) <= 76 and spk [0 ] + 1 == len (spk ):
252
+ # Single fixed push of 1-75 bytes
253
+ return True
254
+ return False
255
+
226
256
class Generate :
227
257
INTERVAL = 600.0 * 2016 / 2015 # 10 minutes, adjusted for the off-by-one bug
228
258
@@ -323,14 +353,22 @@ class Generate:
323
353
return tmpl
324
354
325
355
def mine (self , bcli , grind_cmd , tmpl , reward_spk ):
326
- psbt = generate_psbt (tmpl , reward_spk , blocktime = self .mine_time , poolid = self .poolid )
327
- input_stream = os .linesep .join ([psbt , "true" , "ALL" ]).encode ('utf8' )
328
- psbt_signed = json .loads (bcli ("-stdin" , "walletprocesspsbt" , input = input_stream ))
329
- if not psbt_signed .get ("complete" ,False ):
330
- logging .debug ("Generated PSBT: %s" % (psbt ,))
331
- sys .stderr .write ("PSBT signing failed\n " )
332
- return None
333
- block , signet_solution = decode_psbt (psbt_signed ["psbt" ])
356
+ block = new_block (tmpl , reward_spk , blocktime = self .mine_time , poolid = self .poolid )
357
+
358
+ signet_spk = tmpl ["signet_challenge" ]
359
+ if trivial_challenge (signet_spk ):
360
+ signet_solution = None
361
+ else :
362
+ psbt = generate_psbt (block , signet_spk )
363
+ input_stream = os .linesep .join ([psbt , "true" , "ALL" ]).encode ('utf8' )
364
+ psbt_signed = json .loads (bcli ("-stdin" , "walletprocesspsbt" , input = input_stream ))
365
+ if not psbt_signed .get ("complete" ,False ):
366
+ logging .debug ("Generated PSBT: %s" % (psbt ,))
367
+ sys .stderr .write ("PSBT signing failed\n " )
368
+ return None
369
+ psbt = decode_challenge_psbt (psbt_signed ["psbt" ])
370
+ signet_solution = get_solution_from_psbt (psbt )
371
+
334
372
return finish_block (block , signet_solution , grind_cmd )
335
373
336
374
def do_generate (args ):
0 commit comments