@@ -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
@@ -77,15 +76,20 @@ def decode_challenge_psbt(b64psbt):
77
76
def get_block_from_psbt (psbt ):
78
77
return from_binary (CBlock , psbt .g .map [PSBT_SIGNET_BLOCK ])
79
78
80
- def get_solution_from_psbt (psbt ):
79
+ def get_solution_from_psbt (psbt , emptyok = False ):
81
80
scriptSig = psbt .i [0 ].map .get (PSBT_IN_FINAL_SCRIPTSIG , b"" )
82
81
scriptWitness = psbt .i [0 ].map .get (PSBT_IN_FINAL_SCRIPTWITNESS , b"\x00 " )
82
+ if emptyok and len (scriptSig ) == 0 and scriptWitness == b"\x00 " :
83
+ return None
83
84
return ser_string (scriptSig ) + scriptWitness
84
85
85
86
def finish_block (block , signet_solution , grind_cmd ):
86
- block .vtx [0 ].vout [- 1 ].scriptPubKey += CScriptOp .encode_op_pushdata (SIGNET_HEADER + signet_solution )
87
- block .vtx [0 ].rehash ()
88
- 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 ()
89
93
if grind_cmd is None :
90
94
block .solve ()
91
95
else :
@@ -97,10 +101,7 @@ def finish_block(block, signet_solution, grind_cmd):
97
101
block .rehash ()
98
102
return block
99
103
100
- def generate_psbt (tmpl , reward_spk , * , blocktime = None , poolid = None ):
101
- signet_spk = tmpl ["signet_challenge" ]
102
- signet_spk_bin = bytes .fromhex (signet_spk )
103
-
104
+ def new_block (tmpl , reward_spk , * , blocktime = None , poolid = None ):
104
105
scriptSig = script_BIP34_coinbase_height (tmpl ["height" ])
105
106
if poolid is not None :
106
107
scriptSig = CScript (b"" + scriptSig + CScriptOp .encode_op_pushdata (poolid ))
@@ -129,8 +130,14 @@ def generate_psbt(tmpl, reward_spk, *, blocktime=None, poolid=None):
129
130
block .vtx [0 ].wit .vtxinwit = [cbwit ]
130
131
block .vtx [0 ].vout .append (CTxOut (0 , bytes (get_witness_script (witroot , witnonce ))))
131
132
132
- signme , spendme = signet_txs (block , signet_spk_bin )
133
+ block .vtx [0 ].rehash ()
134
+ block .hashMerkleRoot = block .calc_merkle_root ()
133
135
136
+ return block
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 )
134
141
psbt = PSBT ()
135
142
psbt .g = PSBTMap ( {PSBT_GLOBAL_UNSIGNED_TX : signme .serialize (),
136
143
PSBT_SIGNET_BLOCK : block .serialize ()
@@ -179,14 +186,16 @@ def get_reward_addr_spk(args, height):
179
186
def do_genpsbt (args ):
180
187
poolid = get_poolid (args )
181
188
tmpl = json .load (sys .stdin )
189
+ signet_spk = tmpl ["signet_challenge" ]
182
190
_ , reward_spk = get_reward_addr_spk (args , tmpl ["height" ])
183
- psbt = generate_psbt (tmpl , reward_spk , poolid = poolid )
191
+ block = new_block (tmpl , reward_spk , poolid = poolid )
192
+ psbt = generate_psbt (block , signet_spk )
184
193
print (psbt )
185
194
186
195
def do_solvepsbt (args ):
187
196
psbt = decode_challenge_psbt (sys .stdin .read ())
188
197
block = get_block_from_psbt (psbt )
189
- signet_solution = get_solution_from_psbt (psbt )
198
+ signet_solution = get_solution_from_psbt (psbt , emptyok = True )
190
199
block = finish_block (block , signet_solution , args .grind_cmd )
191
200
print (block .serialize ().hex ())
192
201
@@ -229,6 +238,21 @@ def seconds_to_hms(s):
229
238
out = "-" + out
230
239
return out
231
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
+
232
256
class Generate :
233
257
INTERVAL = 600.0 * 2016 / 2015 # 10 minutes, adjusted for the off-by-one bug
234
258
@@ -329,16 +353,22 @@ class Generate:
329
353
return tmpl
330
354
331
355
def mine (self , bcli , grind_cmd , tmpl , reward_spk ):
332
- psbt = generate_psbt (tmpl , reward_spk , blocktime = self .mine_time , poolid = self .poolid )
333
- input_stream = os .linesep .join ([psbt , "true" , "ALL" ]).encode ('utf8' )
334
- psbt_signed = json .loads (bcli ("-stdin" , "walletprocesspsbt" , input = input_stream ))
335
- if not psbt_signed .get ("complete" ,False ):
336
- logging .debug ("Generated PSBT: %s" % (psbt ,))
337
- sys .stderr .write ("PSBT signing failed\n " )
338
- return None
339
- psbt = decode_challenge_psbt (psbt_signed ["psbt" ])
340
- block = get_block_from_psbt (psbt )
341
- signet_solution = get_solution_from_psbt (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
+
342
372
return finish_block (block , signet_solution , grind_cmd )
343
373
344
374
def do_generate (args ):
0 commit comments