def check_sign(self, blinded_tx: CTransaction, signed_tx: CTransaction, bundle: Dict[str, Any]) -> None: tx_to_sign = blinded_tx.to_mutable() for n, vin in enumerate(tx_to_sign.vin): utxo = bundle['vin_utxo'][n] amount = -1 if utxo['amount'] == -1 else coins_to_satoshi( utxo['amount']) scriptPubKey = CScript(x(utxo['scriptPubKey'])) a = CCoinAddress(utxo['address']) if 'privkey' in utxo: privkey = CCoinKey(utxo['privkey']) assert isinstance(a, P2PKHCoinAddress),\ "only P2PKH is supported for single-sig" assert a == P2PKHElementsAddress.from_pubkey(privkey.pub) assert scriptPubKey == a.to_scriptPubKey() sighash = SignatureHash(scriptPubKey, tx_to_sign, n, SIGHASH_ALL, amount=amount, sigversion=SIGVERSION_BASE) sig = privkey.sign(sighash) + bytes([SIGHASH_ALL]) tx_to_sign.vin[n].scriptSig = CScript( [CScript(sig), CScript(privkey.pub)]) else: pk_list = [CCoinKey(pk) for pk in utxo['privkey_list']] redeem_script_data = [utxo['num_p2sh_participants']] redeem_script_data.extend([pk.pub for pk in pk_list]) redeem_script_data.extend([len(pk_list), OP_CHECKMULTISIG]) redeem_script = CScript(redeem_script_data) assert isinstance(a, P2SHCoinAddress),\ "only P2SH is supported for multi-sig." assert scriptPubKey == redeem_script.to_p2sh_scriptPubKey() assert a == P2SHElementsAddress.from_scriptPubKey( redeem_script.to_p2sh_scriptPubKey()) sighash = SignatureHash(redeem_script, tx_to_sign, n, SIGHASH_ALL, amount=amount, sigversion=SIGVERSION_BASE) sigs = [ pk.sign(sighash) + bytes([SIGHASH_ALL]) for pk in pk_list ] tx_to_sign.vin[n].scriptSig = CScript([b''] + sigs + [redeem_script]) VerifyScript(tx_to_sign.vin[n].scriptSig, scriptPubKey, tx_to_sign, n, amount=amount) self.assertEqual(tx_to_sign.serialize(), signed_tx.serialize())
def from_redeemScript(cls: Type[T_P2SHCoinAddress], redeemScript: CScript) -> T_P2SHCoinAddress: """Convert a redeemScript to a P2SH address Convenience function: equivalent to P2SHBitcoinAddress.from_scriptPubKey(redeemScript.to_p2sh_scriptPubKey()) """ return cls.from_scriptPubKey(redeemScript.to_p2sh_scriptPubKey())
def sign_input(tx, input_index, utxo): """Sign an input of transaction. Single-signature signing with SIGHASH_ALL""" key = utxo['key'] src_addr = CCoinAddress(utxo['address']) script_for_sighash = CScript( [OP_DUP, OP_HASH160, Hash160(key.pub), OP_EQUALVERIFY, OP_CHECKSIG]) assert isinstance(src_addr, (P2PKHCoinAddress, P2SHCoinAddress, P2WPKHCoinAddress)),\ 'only p2pkh, p2wpkh and p2sh_p2wpkh addresses are supported' if isinstance(src_addr, P2PKHCoinAddress): sigversion = SIGVERSION_BASE else: sigversion = SIGVERSION_WITNESS_V0 if 'amountcommitment' in utxo: amountcommitment = CConfidentialValue(x(utxo['amountcommitment'])) else: amountcommitment = CConfidentialValue(coins_to_satoshi(utxo['amount'])) sighash = script_for_sighash.sighash(tx, input_index, SIGHASH_ALL, amount=amountcommitment, sigversion=sigversion) sig = key.sign(sighash) + bytes([SIGHASH_ALL]) if isinstance(src_addr, P2PKHCoinAddress): tx.vin[input_index].scriptSig = CScript( [CScript(sig), CScript(key.pub)]) scriptpubkey = src_addr.to_scriptPubKey() elif isinstance(src_addr, P2WPKHCoinAddress): tx.vin[input_index].scriptSig = CScript() tx.wit.vtxinwit[input_index] = CTxInWitness( CScriptWitness([CScript(sig), CScript(key.pub)])) scriptpubkey = src_addr.to_scriptPubKey() else: # Assume that this is p2sh-wrapped p2wpkh address inner_scriptPubKey = CScript([0, Hash160(key.pub)]) tx.vin[input_index].scriptSig = CScript([inner_scriptPubKey]) tx.wit.vtxinwit[input_index] = CTxInWitness( CScriptWitness([CScript(sig), CScript(key.pub)])) scriptpubkey = inner_scriptPubKey.to_p2sh_scriptPubKey() VerifyScript(tx.vin[input_index].scriptSig, scriptpubkey, tx, input_index, amount=amountcommitment, flags=(SCRIPT_VERIFY_P2SH, ))
def p2sh_multisig(self): source = self.next_address() self.fund_address(source, 0.1) # construct transaction manually tx_ins = [CMutableTxIn(COutPoint(source.txid, source.vout))] keys = [self.next_address().key for _ in range(3)] redeem_script = CScript([OP_2, keys[0].pub, keys[1].pub, keys[2].pub, OP_3, OP_CHECKMULTISIG]) tx_outs = [ CMutableTxOut(Coin(0.1 - self.fee).satoshi(), redeem_script.to_p2sh_scriptPubKey())] tx = CMutableTransaction(tx_ins, tx_outs) # sign and submit key = source.key script = source.address.to_scriptPubKey() sig = self._sign(script, tx, 0, Coin(source.value).satoshi(), key) tx_ins[0].scriptSig = CScript([sig, key.pub]) txid = self._send_transaction(tx, []) self.log_value("p2sh-multisig-tx", txid) # Redeem Transaction tx_ins = [CMutableTxIn(COutPoint(lx(txid), 0))] destination = self.next_address() tx_outs = [CMutableTxOut(Coin(0.1 - 2 * self.fee).satoshi(), destination.address.to_scriptPubKey())] tx = CMutableTransaction(tx_ins, tx_outs) # Sign with 2 out of three keys sig1 = self._sign(redeem_script, tx, 0, Coin(0.1 - self.fee).satoshi(), keys[0], "p2sh") sig3 = self._sign(redeem_script, tx, 0, Coin(0.1 - self.fee).satoshi(), keys[2], "p2sh") tx_ins[0].scriptSig = CScript([OP_0, sig1, sig3, redeem_script]) txid = self._send_transaction(tx, []) self.log_value("p2sh-multisig-redeem-tx", txid) self.generate_block()
def T(redeemScript, expected_hex_bytes): redeemScript = CScript(redeemScript) actual_script = redeemScript.to_p2sh_scriptPubKey() self.assertEqual(b2x(actual_script), expected_hex_bytes)
script_for_sighash = CScript( [OP_DUP, OP_HASH160, Hash160(key.pub), OP_EQUALVERIFY, OP_CHECKSIG]) sighash = script_for_sighash.sighash(tx, input_index, SIGHASH_ALL, amount=utxo.nValue, sigversion=SIGVERSION_WITNESS_V0) sig = key.sign(sighash) + bytes([SIGHASH_ALL]) inner_scriptPubKey = CScript([0, Hash160(key.pub)]) tx.vin[input_index].scriptSig = CScript([inner_scriptPubKey]) tx.wit.vtxinwit[input_index] = CTxInWitness( CScriptWitness([CScript(sig), CScript(key.pub)])) scriptpubkey = inner_scriptPubKey.to_p2sh_scriptPubKey() VerifyScript(tx.vin[input_index].scriptSig, scriptpubkey, tx, input_index, amount=utxo.nValue, flags=(SCRIPT_VERIFY_P2SH, )) sys.stderr.write("Successfully signed\n") # Print out blinded and signed transaction, hex-encoded. print(b2x(tx.serialize()))
def T(redeemScript_ops: List[Union[int, bytes]], expected_hex_bytes: str) -> None: redeemScript = CScript(redeemScript_ops) actual_script = redeemScript.to_p2sh_scriptPubKey() self.assertEqual(b2x(actual_script), expected_hex_bytes)
COIN = CoreCoinParams.COIN # Create the (in)famous correct brainwallet secret key. h = hashlib.sha256(b'correct horse battery staple').digest() seckey = CBitcoinKey.from_secret_bytes(h) # Create a redeemScript. Similar to a scriptPubKey the redeemScript must be # satisfied for the funds to be spent. txin_redeemScript = CScript([seckey.pub, OP_CHECKSIG]) print(b2x(txin_redeemScript)) # Create the magic P2SH scriptPubKey format from that redeemScript. You should # look at the CScript.to_p2sh_scriptPubKey() function in bitcointx.core.script # to understand what's happening, as well as read BIP16: # https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey() # Convert the P2SH scriptPubKey to a base58 Bitcoin address and print it. # You'll need to send some funds to it to create a txout to spend. txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey) print('Pay to:', str(txin_p2sh_address)) # Same as the txid:vout the createrawtransaction RPC call requires # # lx() takes *little-endian* hex and converts it to bytes; in Bitcoin # transaction hashes are shown little-endian rather than the usual big-endian. # There's also a corresponding x() convenience function that takes big-endian # hex and converts it to bytes. txid = lx('bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae') vout = 0