@@ -18,7 +18,7 @@ PATH_BASE_CONTRIB_SIGNET = os.path.abspath(os.path.dirname(os.path.realpath(__fi
1818PATH_BASE_TEST_FUNCTIONAL = os .path .abspath (os .path .join (PATH_BASE_CONTRIB_SIGNET , ".." , ".." , "test" , "functional" ))
1919sys .path .insert (0 , PATH_BASE_TEST_FUNCTIONAL )
2020
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
2222from 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
2323from 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
2424from test_framework .script import CScript , CScriptOp # noqa: E402
@@ -28,7 +28,6 @@ logging.basicConfig(
2828 level = logging .INFO ,
2929 datefmt = '%Y-%m-%d %H:%M:%S' )
3030
31- SIGNET_HEADER = b"\xec \xc7 \xda \xa2 "
3231PSBT_SIGNET_BLOCK = b"\xfc \x06 signetb" # proprietary PSBT global field holding the block being signed
3332RE_MULTIMINER = re .compile (r"^(\d+)(-(\d+))?/(\d+)$" )
3433
@@ -77,15 +76,20 @@ def decode_challenge_psbt(b64psbt):
7776def get_block_from_psbt (psbt ):
7877 return from_binary (CBlock , psbt .g .map [PSBT_SIGNET_BLOCK ])
7978
80- def get_solution_from_psbt (psbt ):
79+ def get_solution_from_psbt (psbt , emptyok = False ):
8180 scriptSig = psbt .i [0 ].map .get (PSBT_IN_FINAL_SCRIPTSIG , b"" )
8281 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
8384 return ser_string (scriptSig ) + scriptWitness
8485
8586def 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 ()
8993 if grind_cmd is None :
9094 block .solve ()
9195 else :
@@ -97,10 +101,7 @@ def finish_block(block, signet_solution, grind_cmd):
97101 block .rehash ()
98102 return block
99103
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 ):
104105 scriptSig = script_BIP34_coinbase_height (tmpl ["height" ])
105106 if poolid is not None :
106107 scriptSig = CScript (b"" + scriptSig + CScriptOp .encode_op_pushdata (poolid ))
@@ -129,8 +130,14 @@ def generate_psbt(tmpl, reward_spk, *, blocktime=None, poolid=None):
129130 block .vtx [0 ].wit .vtxinwit = [cbwit ]
130131 block .vtx [0 ].vout .append (CTxOut (0 , bytes (get_witness_script (witroot , witnonce ))))
131132
132- signme , spendme = signet_txs (block , signet_spk_bin )
133+ block .vtx [0 ].rehash ()
134+ block .hashMerkleRoot = block .calc_merkle_root ()
133135
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 )
134141 psbt = PSBT ()
135142 psbt .g = PSBTMap ( {PSBT_GLOBAL_UNSIGNED_TX : signme .serialize (),
136143 PSBT_SIGNET_BLOCK : block .serialize ()
@@ -179,14 +186,16 @@ def get_reward_addr_spk(args, height):
179186def do_genpsbt (args ):
180187 poolid = get_poolid (args )
181188 tmpl = json .load (sys .stdin )
189+ signet_spk = tmpl ["signet_challenge" ]
182190 _ , 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 )
184193 print (psbt )
185194
186195def do_solvepsbt (args ):
187196 psbt = decode_challenge_psbt (sys .stdin .read ())
188197 block = get_block_from_psbt (psbt )
189- signet_solution = get_solution_from_psbt (psbt )
198+ signet_solution = get_solution_from_psbt (psbt , emptyok = True )
190199 block = finish_block (block , signet_solution , args .grind_cmd )
191200 print (block .serialize ().hex ())
192201
@@ -229,6 +238,21 @@ def seconds_to_hms(s):
229238 out = "-" + out
230239 return out
231240
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+
232256class Generate :
233257 INTERVAL = 600.0 * 2016 / 2015 # 10 minutes, adjusted for the off-by-one bug
234258
@@ -329,16 +353,22 @@ class Generate:
329353 return tmpl
330354
331355 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+
342372 return finish_block (block , signet_solution , grind_cmd )
343373
344374def do_generate (args ):
0 commit comments