def test_parse_hash(self): self.assertEqual(addr.parse_hash(helpers.OP_IF['p2sh']), helpers.OP_IF['script_hash']) self.assertEqual(addr.parse_hash(helpers.MSIG_2_2['p2sh']), helpers.MSIG_2_2['script_hash']) self.assertEqual( addr.parse_hash( helpers.P2WSH['human']['ins'][0]['addr']), helpers.P2WSH['ser']['ins'][0]['pk_script'][2:]) self.assertEqual(addr.parse_hash(helpers.P2WPKH_ADDR['address']), helpers.P2WPKH_ADDR['pkh']) self.assertEqual(addr.parse_hash(helpers.ADDR[0]['p2pkh']), helpers.PK['ser'][0]['pkh']) with self.assertRaises(ValueError) as context: addr.parse('bc1blahblahblah') self.assertIn('Unsupported address format. Got: ', str(context.exception)) # Test cash addr code riemann.select_network('bitcoin_cash_main') self.assertEqual( addr.parse_hash(helpers.OP_IF['p2sh']), helpers.OP_IF['script_hash']) self.assertEqual( addr.parse_hash(helpers.OP_IF['cashaddr']), helpers.OP_IF['script_hash']) self.assertEqual( addr.parse_hash(helpers.CASHADDR['p2pkh']), utils.hash160(helpers.CASHADDR['pubkey']))
def make_wpkh_witness( t: tx.Tx, input_index: int, prevout_value: int, privkey: bytes ) -> tx.InputWitness: ''' Make a witness for a transaction spending a native-Segwith WPKH output. Args: t: A transaction. You can use the output of `spend_utxo_to_address` input_index: Which input should this function sign? prevout_value: What is the size of the UTXO being spent? privkey: The 32-byte private key to sign with Return: A SegWit witness signing `t` ''' pubkey = crypto.priv_to_pub(privkey) sighash = t.sighash_all( index=input_index, script=b'\x16\x00\x14' + rutils.hash160(pubkey), prevout_value=rutils.i2le_padded(prevout_value, 8)) signature = crypto.sign_digest(sighash, privkey) return tx_builder.make_witness([signature, pubkey])
def partial(tx_id: str, index: int, prevout_value: int, recipient_addr: str, output_value: int, lock_time: int, keypair: KeyPair) -> Tx: ''' Makes a partial_tx from human readable information Args: tx_id (str): txid of parent tx index (int): index of input in parent tx prevout_value (int): value in satoshi of the input recipient_addr (str): address of the recipient output_value (int): value in satoshi of the output lock_time (int): desired lock_time in bitcoin format keypair (tuple(str, str)): privkey as hex, pubkey as hex Returns: (riemann.tx.Tx): The signed transaction ''' outpoint = simple.outpoint(tx_id, index) pub = bytes.fromhex(keypair[1]) pkh = rutils.hash160(pub) output_script = b'\x19\x76\xa9\x14' + pkh + b'\x88\xac' # Assume PKH unsigned = make_partial_tx(outpoint=outpoint, output_value=output_value, output_address=recipient_addr, lock_time=lock_time) signed = sign_partial_tx(partial_tx=unsigned, keypair=keypair, prevout_script=output_script, prevout_value=rutils.i2le_padded( prevout_value, 8)) return signed
def make_pkh_output_script(pubkey: bytes, witness: bool = False) -> bytes: ''' Makes a P2PKH or P2WPKH script pubkey from a raw public key. Does not support Compatibility p2wpkh-via-p2sh output scripts. Args: pubkey: The 33- or 65-byte public key. witness: Pass True to make a P2WSH script pubkey. Returns: The script pubkey containing the hash of the pubkey. ''' if witness and not riemann.network.SEGWIT: raise ValueError('Network {} does not support witness scripts.'.format( riemann.get_current_network_name())) output_script = bytearray() if type(pubkey) is not bytearray and type(pubkey) is not bytes: raise ValueError('Unknown pubkey format. ' 'Expected bytes. Got: {}'.format(type(pubkey))) pubkey_hash = utils.hash160(pubkey) if witness: output_script.extend(riemann.network.P2WPKH_PREFIX) output_script.extend(pubkey_hash) else: output_script.extend(b'\x76\xa9\x14') # OP_DUP OP_HASH160 PUSH14 output_script.extend(pubkey_hash) output_script.extend(b'\x88\xac') # OP_EQUALVERIFY OP_CHECKSIG return bytes(output_script)
def make_pkh_output_script(pubkey, witness=False): ''' bytearray -> bytearray ''' if witness and not riemann.network.SEGWIT: raise ValueError( 'Network {} does not support witness scripts.' .format(riemann.get_current_network_name())) output_script = bytearray() if type(pubkey) is not bytearray and type(pubkey) is not bytes: raise ValueError('Unknown pubkey format. ' 'Expected bytes. Got: {}'.format(type(pubkey))) pubkey_hash = utils.hash160(pubkey) if witness: output_script.extend(riemann.network.P2WPKH_PREFIX) output_script.extend(pubkey_hash) else: output_script.extend(b'\x76\xa9\x14') # OP_DUP OP_HASH160 PUSH14 output_script.extend(pubkey_hash) output_script.extend(b'\x88\xac') # OP_EQUALVERIFY OP_CHECKSIG return output_script
def test_parse_hash(self): self.assertEqual(addr.parse_hash(helpers.OP_IF_P2SH), helpers.OP_IF_SCRIPT_HASH) self.assertEqual(addr.parse_hash(helpers.MSIG_TWO_TWO_P2SH), helpers.MSIG_TWO_TWO_SCRIPT_HASH) self.assertEqual(addr.parse_hash(helpers.P2WSH_ADDRESS), helpers.P2WSH_SCRIPT_HASH) self.assertEqual(addr.parse_hash(helpers.P2WPKH_ADDRESS), helpers.P2WPKH_PKH) self.assertEqual(addr.parse_hash(helpers.P2PKH_0), helpers.PKH_0) with self.assertRaises(ValueError) as context: addr.parse('bc1blahblahblah') self.assertIn('Unsupported address format. Got: ', str(context.exception)) # Test cash addr code riemann.select_network('bitcoin_cash_main') self.assertEqual(addr.parse_hash(helpers.OP_IF_P2SH), helpers.OP_IF_SCRIPT_HASH) self.assertEqual(addr.parse_hash(helpers.OP_IF_CASHADDR), helpers.OP_IF_SCRIPT_HASH) self.assertEqual(addr.parse_hash(helpers.CASHADDR_P2PKH_ADDRESS), utils.hash160(helpers.CASHADDR_PUBKEY))
def make_pkh_address(pubkey: bytes, witness: bool = False, cashaddr: bool = True) -> str: '''Turns a pubkey into an address''' pubkey_hash = utils.hash160(pubkey) return _make_pkh_address(pubkey_hash=pubkey_hash, witness=witness, cashaddr=cashaddr)
def _ser_script_to_sh_address(script_bytes: bytes, witness: bool = False, cashaddr: bool = True) -> str: '''makes a p2sh address from a serialized script''' if witness: script_hash = utils.sha256(script_bytes) else: script_hash = utils.hash160(script_bytes) return _hash_to_sh_address(script_hash=script_hash, witness=witness, cashaddr=cashaddr)
def make_sh_script_pubkey(script_bytes: bytes, witness: bool = False) -> bytes: output_script = bytearray() if witness: script_hash = utils.sha256(script_bytes) output_script.extend(riemann.network.P2WSH_PREFIX) output_script.extend(script_hash) else: script_hash = utils.hash160(script_bytes) output_script.extend(b'\xa9\x14') # OP_HASH160 PUSH0x14 output_script.extend(script_hash) output_script.extend(b'\x87') # OP_EQUAL return bytes(output_script)
def make_and_sign_split_tx(tx_id: str, index: int, prevout_value: int, control_addr: str, control_addr_keypair: Tuple[str, str], num_auctions: int, change_addr: str) -> tx.Tx: ''' Makes and signs a transaction with several small outputs Args: tx_id: the input prevout's txid index: the input prevout's index prevout_value: the input prevout's value control_addr: the input prevout's controlling address control_addr_keypair: the priv/pub keypair as a tuple of hex num_auctions: how many outputs to make change_addr: where to send leftover funds ''' # Split in 10, send SUMMA change split_tx = us.generate_small_utxos(tx_id=tx_id, index=index, prevout_value=prevout_value, recipient_addr=control_addr, num_outputs=num_auctions, change_addr=change_addr, fee=8000, size=550) pubkeyhash = rutils.hash160(bytes.fromhex(control_addr_keypair[1])) prevout_script = b'\x19\x76\xa9\x14' + pubkeyhash + b'\x88\xac' sighash_bytes = split_tx.sighash_single(index=0, script=prevout_script, prevout_value=rutils.i2le_padded( prevout_value, 8), anyone_can_pay=True) sig = utils.sign_hash(sighash_bytes, control_addr_keypair[0]) sig = '{}{}'.format(sig, '83') # Build the witness wit = tx.make_witness( [bytes.fromhex(sig), bytes.fromhex(control_addr_keypair[1])]) tx_witnesses = [wit] split_tx = split_tx.copy(tx_witnesses=tx_witnesses) return split_tx
def _make_child_xpub(derivation: str, parent_or_none: Optional[LedgerPubkey], child: LedgerPubkey, mainnet: bool = True) -> str: ''' Builds an xpub for a derived child using its parent and path Args: derivation (str): the m-prefixed derivation path e.g. m/44h/0h/0h parent (LedgerPubkey): the parent public key child (LedgerPubkey): the child public key mainnet (bool): whether to use mainnet prefixes ''' indices = utils.parse_derivation(derivation) # determine appropriate xpub version bytes if not mainnet: prefix = utils.VERSION_BYTES['testnet']['public'] else: prefix = utils.VERSION_BYTES['mainnet']['public'] if parent_or_none is not None: # xpubs include the parent fingerprint parent = cast(LedgerPubkey, parent_or_none) compressed_parent_key = utils.compress_pubkey(parent['pubkey']) parent_fingerprint = rutils.hash160(compressed_parent_key)[:4] child_index = indices[-1].to_bytes(4, byteorder='big') depth = len(indices) else: # this means it's a master key parent_fingerprint = b'\x00' * 4 child_index = b'\x00' * 4 depth = 0 # xpubs always use compressed pubkeys compressed_pubkey = utils.compress_pubkey(child['pubkey']) # build the xpub xpub = bytearray() xpub.extend(prefix) # xpub prefix xpub.extend([depth]) # depth xpub.extend(parent_fingerprint) # paren't fingerprint xpub.extend(child_index) # index xpub.extend(child['chain_code']) # chain_code xpub.extend(compressed_pubkey) # pubkey (comp) return base58.encode(xpub)
def make_pkh_address(pubkey: bytes, witness: bool = False, cashaddr: bool = True) -> str: ''' Turns a pubkey into an address. Prefers Cashaddrs to legacy addresses whenever supported. Args: pubkey: The 33 or 65 byte public key witness: Pass True to generate a witness address if supported. Default False. cashaddr: Pass False to prefer legacy to cashaddr. Default True Returns: The encoded address ''' pubkey_hash = utils.hash160(pubkey) return _make_pkh_address(pubkey_hash=pubkey_hash, witness=witness, cashaddr=cashaddr)
def validate_address(address: AddressEntry) -> bool: ''' Validates the address data structure ''' try: h = addr.parse_hash(address['address']) if address['script'] == b'': return True if address['script_pubkeys'] != pubkeys_from_script(address['script']): return False if h in [ rutils.sha256(address['script']), # p2wsh rutils.hash160(address['script']) ]: # p2sh return True except (ValueError, TypeError, KeyError): pass return False
def undo_split(tx_id: str, num_auctions: int, change_addr: str, control_addr_keypair: Tuple[str, str]) -> tx.Tx: ''' undoes a split tx. NOT FOR SHUTTING DOWN AUCTIONS Args: tx_id: the tx_id of the split tx num_auctions: the number of non-change outputs of the split tx change_addr: the address to send leftovers to control_addr_keypair: the keypair of the controlling address ''' tx_ins = [ simple.unsigned_input(simple.outpoint(tx_id, i)) for i in range(num_auctions) ] tx_outs = [simple.output(600, change_addr)] unsplit_tx = simple.unsigned_witness_tx(tx_ins, tx_outs) pubkeyhash = rutils.hash160(bytes.fromhex(control_addr_keypair[1])) prevout_script = b'\x19\x76\xa9\x14' + pubkeyhash + b'\x88\xac' tx_witnesses = [] for i in range(num_auctions): sighash_bytes = unsplit_tx.sighash_all( index=i, script=prevout_script, prevout_value=rutils.i2le_padded(550, 8), anyone_can_pay=False) sig = utils.sign_hash(sighash_bytes, control_addr_keypair[0]) sig = '{}{}'.format(sig, '01') # Build the witness wit = tx.make_witness( [bytes.fromhex(sig), bytes.fromhex(control_addr_keypair[1])]) tx_witnesses.append(wit) return cast(tx.Tx, unsplit_tx.copy(tx_witnesses=tx_witnesses))
def _ser_script_to_sh_address(script_bytes: bytes, witness: bool = False, cashaddr: bool = True) -> str: ''' Turns a serialized script into a SH address. Prefers Cashaddrs to legacy addresses whenever supported. Args: script_bytes: The serialized script, as a bytestring witness: Pass True to generate a witness address if supported. Default False. cashaddr: Pass False to prefer legacy to cashaddr. Default True Returns: The encoded address ''' if witness: script_hash = utils.sha256(script_bytes) else: script_hash = utils.hash160(script_bytes) return _hash_to_sh_address(script_hash=script_hash, witness=witness, cashaddr=cashaddr)
def make_sh_script_pubkey(script_bytes: bytes, witness: bool = False) -> bytes: ''' Make a P2SH or P2WSH script pubkey from a serialized script. Does not support Compatibility p2wsh-via-p2sh output scripts. Args: script_bytes: The serialized redeem script or witness script. witness: Pass True to make a P2WSH script pubkey. Returns: The script pubkey containing the hash of the serialized script. ''' output_script = bytearray() if witness: script_hash = utils.sha256(script_bytes) output_script.extend(riemann.network.P2WSH_PREFIX) output_script.extend(script_hash) else: script_hash = utils.hash160(script_bytes) output_script.extend(b'\xa9\x14') # OP_HASH160 PUSH0x14 output_script.extend(script_hash) output_script.extend(b'\x87') # OP_EQUAL return bytes(output_script)
def _signable(key: bytes, prevout_info: PrevoutInfo) -> bool: ''' Determines if the key or its hash is in the PrevoutInfo We use this to determine whether we should get a signature for an input Args: key (bytes): the public key prevout_info (PrevoutInfo): dict of script and value for the prevout Returns: (bool): True if signable, false otherwise ''' if len(key) in [64, 65]: key = utils.compress_pubkey(key) # enforce compression # if there's no script, it's not signable if prevout_info['witness_script'] is None: return False # if the key is anywhere in the script, it is signable script = cast(bytes, prevout_info['witness_script']) if (key in script or rutils.hash160(key) in script): return True return False
def compute_pk_hash(pubkey_hex): return utils.hash160(bytes.fromhex(pubkey_hex)).hex()
def make_btc_shutdown_txns(auction_tx_id: str, idxs: List[int], add_funds_tx_id: str, add_funds_idx: int, add_funds_value: int, control_addr: str, control_addr_keypair: Tuple[str, str], change_addr: str, eth_addr: str, fee: int = 7700) -> List[str]: ''' Shuts down an auction by winning them with the owner keypair Args: tx_id: the split tx for the auction set idxs: the unpurchased indexes add_funds_tx_id: a prevout tx id to fund these transactions add_funds_idx: the prevout index add_funds_value: the prevout value control_addr: the input prevout's controlling address control_addr_keypair: the priv/pub keypair as a tuple of hex change_addr: where to send leftover funds eth_addr: where to deliver auction proceeds fee: the tx fee to pay ''' prev = (add_funds_tx_id, add_funds_idx) val = add_funds_value shutdown_txns = [] pubkeyhash = rutils.hash160(bytes.fromhex(control_addr_keypair[1])) prevout_script = b'\x19\x76\xa9\x14' + pubkeyhash + b'\x88\xac' for i in range(len(idxs)): tx_ins = [ simple.unsigned_input(simple.outpoint(auction_tx_id, idxs[i])), simple.unsigned_input(simple.outpoint(*prev)) ] out_val = val + 550 - fee addr = control_addr if i < len(idxs) - 1 else change_addr tx_outs = [ simple.output(out_val, addr), tx.make_op_return_output(bytes.fromhex(eth_addr[2:])) ] shutdown_tx = simple.unsigned_witness_tx(tx_ins, tx_outs) tx_witnesses = [] sighash_bytes = shutdown_tx.sighash_all( index=0, script=prevout_script, prevout_value=rutils.i2le_padded(550, 8), anyone_can_pay=False) sig = utils.sign_hash(sighash_bytes, control_addr_keypair[0]) sig = '{}{}'.format(sig, '01') # Build the witness wit = tx.make_witness( [bytes.fromhex(sig), bytes.fromhex(control_addr_keypair[1])]) tx_witnesses.append(wit) sighash_bytes_2 = shutdown_tx.sighash_all( index=1, script=prevout_script, prevout_value=rutils.i2le_padded(val, 8), anyone_can_pay=False) sig_2 = utils.sign_hash(sighash_bytes_2, control_addr_keypair[0]) sig_2 = '{}{}'.format(sig_2, '01') # Build the witness wit_2 = tx.make_witness( [bytes.fromhex(sig_2), bytes.fromhex(control_addr_keypair[1])]) tx_witnesses.append(wit_2) prev = (shutdown_tx.tx_id.hex(), 0) val = out_val shutdown_txns.append(shutdown_tx.copy(tx_witnesses=tx_witnesses).hex()) return shutdown_txns
secret_hash = utils.sha256(bytes.fromhex(secret)).hex() # Use real pubkeys! fake_pk_execute = '02' * 32 fake_pk_refund = '03' * 32 # Use a real sig! fake_sig = '04' * 32 # Use a real timelock! timeout = 'deadbeef' # string formatting to fill parameters filled_in_redeem_script = htlc_redeem_script.format( secret_hash=secret_hash, pkh0=utils.hash160(bytes.fromhex(fake_pk_execute)).hex(), timeout=timeout, pkh1=utils.hash160(bytes.fromhex(fake_pk_refund)).hex()) # DON'T SEND MONEY TO THIS EXAMPLE ADDRESS!!! # t 3fKPy737rsshnQJ7iRoXw3XujCB7tjuiUt htlc_address = addr.make_sh_address(filled_in_redeem_script) # how to send money there output = simple.output(500000, htlc_address) # --- HOW TO SPEND --- # fill in the sig/pubkey/secret filled_in_execute_script = htlc_stack_script_execute.format( sig=fake_sig, pk=fake_pk_execute,