def createTxState(tx_in: TxInput, pubkey_left: PublicKey, pubkey_right: PublicKey, pubkey_pay_right: PublicKey, pubkey_mulsig_left: PublicKey, pubkey_mulsig_right: PublicKey, lock_val: float, left_val: float, right_val: float, T: int, delta: int) -> Transaction: """ Move coins from left user balance to a new "lock" output. Before: 'a' coins to L, 'b' coins to R. After: 'a - c' coins to L, 'b' coins to R, 'c' coins locked. :param tx_in: reference to channel open transaction :param pubkey_left: public key owned by left user to receive his coins from channel :param pubkey_right: public key owned by right user to receive his coins from channel :param pubkey_pay_right: public key owned by right user for payment after time T :param pubkey_mulsig_left: public key owned by left user for refund if enable-refund tx is published :param pubkey_mulsig_right: public key owned by right user for refund if enable-refund tx is published :param lock_val: amount of coins to lock: 'c' :param left_val: coins or left user: '******' :param right_val: coins or right user: '******' :param T: locked funds can pe paid after this time wherever right user wants :param delta: upper bound on time for transaction to be confirmed by the network :return: tx_state """ out_lock_script = getTxStateLockScript(T, delta, pubkey_pay_right, pubkey_mulsig_left, pubkey_mulsig_right) tx_out_lock = TxOutput(lock_val, out_lock_script) tx_out_left = TxOutput(left_val, P2pkhAddress(pubkey_left.get_address().to_string()).to_script_pub_key()) tx_out_right = TxOutput(right_val, P2pkhAddress(pubkey_right.get_address().to_string()).to_script_pub_key()) tx = Transaction([tx_in], [tx_out_lock, tx_out_left, tx_out_right]) return tx
def getTxStateLockScript(T: int, delta: int, pubkey_pay_right: PublicKey, pubkey_mulsig_left: PublicKey, pubkey_mulsig_right: PublicKey) -> Script: """ tx_state lock script encodes possibilities to either refund locked coins to left or pay to right :param T: locked funds can pe paid after this time wherever right user wants :param delta: upper bound on time for transaction to be confirmed by the network :param pubkey_pay_right: public key owned by right user for payment after time T :param pubkey_mulsig_left: public key owned by left user for refund if enable-refund tx is published :param pubkey_mulsig_right: public key owned by right user for refund if enable-refund tx is published :return: tx_state lock script """ # signature script: # - for refund (with enable-refund tx + ∆): "OP_0 <left_signature> <right_signature>" # - for payment (time() >= T): "<signature_right> <pubkey_right> OP_0 OP_0 OP_0 OP_0 OP_0 OP_0" lock_script = Script([ 'OP_2', pubkey_mulsig_left.to_hex(), pubkey_mulsig_right.to_hex(), 'OP_2', 'OP_CHECKMULTISIG', 'OP_IF', delta, 'OP_CHECKSEQUENCEVERIFY', 'OP_DROP', 'OP_TRUE', # check if refund and lock for ∆ 'OP_ELSE', T, 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', pubkey_pay_right.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG', # check if payment 'OP_ENDIF' ]) return lock_script
def main(): #Connect to the regtest setup('regtest') proxy = NodeProxy('user', 'bitcoin').get_proxy() #Accept a Public Key from User and derive a P2PKH address from it pk = PublicKey.from_hex(input('Please input your Public Key\n')) pk_p2pkh_addr = pk.get_address() #Accept future absolute block amount from User current_blocks = proxy.getblockcount() absolute_blocks = int( input( f'\nPlease input the future block height. The current block height is: {current_blocks}\n' )) #Set the timelock sequence and create a redeemScript accordingly redeem_script = Script([ absolute_blocks, 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', pk_p2pkh_addr.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG' ]) #Generate a P2SH Address from the redeemScript and show it p2sh_addr = P2shAddress.from_script(redeem_script) print( f'\nThe P2SH Address is:\n{p2sh_addr.to_string()} Now, go on and send some funds to it.\n' )
def tx_decf(self, tx_hash): raw_tx = self.proxy.getrawtransaction( tx_hash) # get the raw transaction from the hash of the tx decoded_tx = self.proxy.decoderawtransaction( raw_tx) # decode the raw transaction and get the json format # check to see if the tx has 1 input and two outputs and is of type pay2pubkeyhash and pay2scripthash if len(decoded_tx['vin']) != 1 or len(decoded_tx['vout']) != 2: return -1, -1 if decoded_tx['vout'][0]['scriptPubKey'][ 'hex'][:6] != '76a914' or decoded_tx['vout'][0][ 'scriptPubKey']['hex'][-4:] != '88ac': #pay2pubkeyhash return -1, -1 if decoded_tx['vout'][1]['scriptPubKey'][ 'hex'][:4] != 'a914' or decoded_tx['vout'][1]['scriptPubKey'][ 'hex'][-2:] != '87': #pay2scripthash return -1, -1 try: vk_u_compressed = decoded_tx['vin'][0]['scriptSig']['hex'][-66:] vk_u = PublicKey(vk_u_compressed).to_hex(compressed=False)[2:] ct_c = decoded_tx['vout'][1]['scriptPubKey']['hex'][4:-2] return vk_u, ct_c except: return -1, -1
def tx_decb(self, block_id, vk_p): txs_hashes = self.get_block_txs( block_id) # Get the hash of transactions inside this block for tx_hash in txs_hashes: raw_tx = self.proxy.getrawtransaction( tx_hash) # get the raw transaction from the hash of the tx decoded_tx = self.proxy.decoderawtransaction( raw_tx) # decode the raw transaction and get the json format # check to see if the tx has 1 input and two outputs and is of type pay2pubkeyhash if len(decoded_tx['vin']) != 1 or len(decoded_tx['vout']) != 2: continue if decoded_tx['vout'][0]['scriptPubKey'][ 'hex'][:6] != '76a914' or decoded_tx['vout'][0][ 'scriptPubKey']['hex'][-4:] != '88ac': # pay2pubkeyhash continue if decoded_tx['vout'][1]['scriptPubKey'][ 'hex'][:4] != 'a914' or decoded_tx['vout'][1][ 'scriptPubKey']['hex'][-2:] != '87': # pay2scripthash continue try: vk_p_compressed = decoded_tx['vin'][0]['scriptSig']['hex'][ -66:] ct_r1 = decoded_tx['vout'][0]['scriptPubKey']['hex'][6:-4] ct_r2 = decoded_tx['vout'][1]['scriptPubKey']['hex'][4:-2] if vk_p_compressed == PublicKey('04' + vk_p).to_hex(compressed=True): print("Found the transaction with response message") return ct_r1 + ct_r2 except: continue return None
def get_redeem_pub_key_obj(): # # Get the private key for the p2pkh of the redeem script # # Using a public key has not been tested. # Support for public from user submitted text looks like it's limited in bitcoin-utils lib. # function_section = 'Function 1' private_key_to = Common.get_config_value(function_section, 'private_key_to') public_key_to = Common.get_config_value(function_section, 'public_key_to') if len(private_key_to) > 0: redeem_pub_key_obj = Common.get_pub_key_from_priv_key_obj( private_key_to) return redeem_pub_key_obj # Not tested and copied the line below from the lib. elif len(public_key_to) > 0: return PublicKey.from_hex( hexlify(public_key_to.to_string()).decode('utf-8')) else: sys.exit( 'No private or public key provided in configuration of function 1!' )
def create_HODL_address(key, lock, is_priv=False): setup(os.getenv("BUY_LAMBO_BTC_NET", "regtest")) # Get public key (given or found from private key) public_key = PrivateKey(key).get_public_key() if is_priv else PublicKey(key) # Get address from public key address_from_key = public_key.get_address() # Set lock sequence prefix seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, lock) # create the redeem script - needed to sign the transaction redeem_script = Script( [ seq.for_script(), "OP_CHECKLOCKTIMEVERIFY", "OP_DROP", "OP_DUP", "OP_HASH160", address_from_key.to_hash160(), "OP_EQUALVERIFY", "OP_CHECKSIG", ] ) # create a P2SH address from a redeem script addr = P2shAddress.from_script(redeem_script) print("Time-locked address: {}".format(addr.to_string()))
def _tx_encf(self, vk_p, ct_c, sk_u, txin_id, txin_index): # create transaction input from tx id of UTXO txin = TxInput(txin_id, txin_index) # having the vk_p from the shared key generate the corresponding address for decoder to use to respond vk_p = PublicKey(vk_p) addr_p = vk_p.get_address().to_string() # create transaction output using P2PKH scriptPubKey for paying address paying_addr = P2pkhAddress(addr_p) paying_txout = TxOutput(0.00002, paying_addr.to_script_pub_key()) # create an output where the address is the ciphertext cipher_txout = TxOutput(0.00001, Script(['OP_HASH160', ct_c, 'OP_EQUAL'])) # create transaction from inputs/outputs -- default locktime is used tx = Transaction([txin], [paying_txout, cipher_txout]) #print("\nRaw unsigned transaction:\n" + tx.serialize()) # use private key corresponding to the address that contains the UTXO we are trying to spend to sign the input vk_u = sk_u.get_public_key() addr_u = vk_u.get_address().to_string() # note that we pass the scriptPubkey as one of the inputs of sign_input because it is used to replace # the scriptSig of the UTXO we are trying to spend when creating the transaction digest from_addr = P2pkhAddress(addr_u) sig = sk_u.sign_input( tx, 0, from_addr.to_script_pub_key()) # 0 is for the index of the input # set the scriptSig (unlocking script) vk_u = vk_u.to_hex() txin.script_sig = Script([sig, vk_u]) signed_tx = tx.serialize() print("Users input address: ", addr_u) print("Decoder paying address: ", addr_p) print("\nRaw signed transaction:\n" + signed_tx) return signed_tx
def main(): # always remember to setup the network setup('regtest') # # This script creates a P2SH address containing a CHECKLOCKTIMEVERIFY plus a P2PKH locking funds with a key as # well as for an absolute amount of blocks or an absolute amount of seconds since the transaction. # parser = argparse.ArgumentParser( description='Give the public key, a future time expressed either in block height or in UNIX Epoch time and ' 'the P2SH address will be displayed') parser.add_argument('pubkey', help="Add the public key.") parser.add_argument('-param', type=int, help="Add the number of blocks or the time expressed in seconds.") args = parser.parse_args() # set values key = args.pubkey absolute_param = args.param p2pkh_pk = PublicKey(key) # set Locktime seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, absolute_param) locktime = Locktime(absolute_param) # get the address (from the public key) p2pkh_addr = p2pkh_pk.get_address() # print("Public key: " + p2pkh_pk.to_hex(compressed=True)) # print("P2PKH Address: " + p2pkh_addr.to_string()) # create the redeem script redeem_script = Script( [seq.for_script(), 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', p2pkh_addr.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG']) # create a P2SH address from a redeem script addr = P2shAddress.from_script(redeem_script) print("The P2SH address is : " + addr.to_string())
def create_p2sh(proxy, block_height=10, address_pubk=None): """ Creates a P2SH address with an absolute block locktime. :param proxy: JSON RPC proxy for connecting to the network. :param block_height: Block height the lock is valid for. Default value is 10. :param address_pubk: Public key of the address locking the funds. If None, a new address will be created. """ # if a public key is not specified, create a new address and display its keys for future use if not address_pubk: print('Public key not provided. Created a new address.') address = proxy.getnewaddress() print('Address:', address) address_privk = proxy.dumpprivkey(address) print('Private key:', address_privk) address_pubk = proxy.getaddressinfo(address)['pubkey'] print('Public key:', address_pubk) # create the public key object p2pkh_pk = PublicKey(address_pubk) # create sequence for the redeem script seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, block_height) # create the redeem script redeem_script = Script([ seq.for_script(), 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', p2pkh_pk.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG' ]) # create the P2SH address from the redeem script p2sh_addr = P2shAddress.from_script(redeem_script) # insert the P2SH address into the wallet proxy.importaddress(p2sh_addr.to_string()) # display the P2SH address print('Created P2SH address:', p2sh_addr.to_string())
def getEnableTxOutputLockScript(pubkey: PublicKey, rel_timelock: int) -> Script: """ Create lock script for output of enable-(payment/refund) transaction :param pubkey: public key owned by corresponding payment participant :param rel_timelock: relative lock on outputs. Should be same for all outputs of transaction :return: lock script """ seq = Sequence(TYPE_RELATIVE_TIMELOCK, rel_timelock) return Script([ seq.for_script(), 'OP_CHECKSEQUENCEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', pubkey.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG' ])
def tx_decf(self, tx_hash): decoded_tx = self.get_tx(tx_hash) #--------------------get the public key of the user------------------------------ if len(decoded_tx['vin']) != 1 or len( decoded_tx['vShieldedOutput']) != 2: return -1, -1 try: vk_u_compressed = decoded_tx['vin'][0]['scriptSig']['hex'][-66:] vk_u = PublicKey(vk_u_compressed).to_hex(compressed=False)[2:] except: return -1, -1 #--------------------get the ciphertext------------------------------------------ cmd = os.popen(zcash_cli + " " + conf_file + ' z_listreceivedbyaddress ' + public_address_decoder_shielded) tx_json = json.loads(cmd.read())[-1] ct_c = tx_json['memo'][:self.challenge_len * 2] self.tx_addr_dict[tx_hash] = bytes.fromhex( tx_json['memo'][self.challenge_len * 2:self.challenge_len * 2 + self.address_len]).decode() return vk_u, ct_c
def main(): # always remember to setup the network setup('mainnet') # create a private key (deterministically) priv = PrivateKey(secret_exponent=1) # compressed is the default print("\nPrivate key WIF:", priv.to_wif(compressed=True)) # could also instantiate from existing WIF key #priv = PrivateKey.from_wif('KwDiBf89qGgbjEhKnhxjUh7LrciVRzI3qYjgd9m7Rfu73SvHnOwn') # get the public key pub = priv.get_public_key() # compressed is the default print("Public key:", pub.to_hex(compressed=True)) # get address from public key address = pub.get_address() # print the address and hash160 - default is compressed address print("Address:", address.to_string()) print("Hash160:", address.to_hash160()) print("\n--------------------------------------\n") # sign a message with the private key and verify it message = "The test!" signature = priv.sign_message(message) print("The message to sign:", message) print("The signature is:", signature) if PublicKey.verify_message(address.to_string(), signature, message): print("The signature is valid!") else: print("The signature is NOT valid!")
def validate_certificate(cert, issuer_identifier, testnet, blockchain_services): filename = os.path.basename(cert) tmp_filename = '__' + filename shutil.copy(cert, tmp_filename) issuer_address = get_issuer_address(tmp_filename) proof = get_and_remove_chainpoint_proof(tmp_filename) if proof == None: os.remove(tmp_filename) return False, "no chainpoint_proof in metadata" # get the hash after removing the metadata filehash = '' with open(tmp_filename, 'rb') as pdf_file: filehash = hashlib.sha256(pdf_file.read()).hexdigest() # instantiate chainpoint object cp = ChainPointV2() txid = cp.get_txid_from_receipt(proof) # make request to get txs regarding this address # issuance is the first element of data_before_issuance data_before_issuance, data_after_issuance = \ network_utils.get_all_op_return_hexes(issuer_address, txid, blockchain_services, testnet) # validate receipt valid, reason = cp.validate_receipt(proof, data_before_issuance[0], filehash, issuer_identifier, testnet) # display error except when the certificate expired; this is because we want # revoked certificate error to be displayed before cert expired error # TODO clean hard-coded reason if not valid and not reason.startswith("certificate expired"): return False, reason # set bitcoin network (required for addr->pkh in revoke address) if testnet: setup('testnet') else: setup('mainnet') # check if cert's issuance is after a revoke address cmd on that address # and if yes then the issuance is invalid (address was revoked) # we check before checking for cert revocations since if the issuance was # after an address revocation it should show that as an invalid reason # 0 index is the actual issuance -- ignore it for i in range(len(data_before_issuance))[1:]: cred_dict = cred_protocol.parse_op_return_hex(data_before_issuance[i]) if cred_dict: if cred_dict['cmd'] == cred_protocol.hex_op('op_revoke_address'): issuer_pkh = P2pkhAddress(issuer_address).to_hash160() if issuer_pkh == cred_dict['data']['pkh']: return False, "address was revoked" # check if cert or batch was revoked from oldest to newest for op_return in reversed(data_after_issuance): cred_dict = cred_protocol.parse_op_return_hex(op_return) if cred_dict: if cred_dict['cmd'] == cred_protocol.hex_op('op_revoke_batch'): if txid == cred_dict['data']['txid']: return False, "batch was revoked" elif cred_dict['cmd'] == cred_protocol.hex_op('op_revoke_creds'): if txid == cred_dict['data']['txid']: # compare the certificate hash bytes filehash_bytes = utils.hex_to_bytes(filehash) ripemd_filehash = utils.ripemd160(filehash_bytes) ripemd_hex = utils.bytes_to_hex(ripemd_filehash) if ripemd_hex == cred_dict['data']['hashes'][0]: return False, "cert hash was revoked" if len(cred_dict['data']['hashes']) > 1: if ripemd_hex == cred_dict['data']['hashes'][1]: return False, "cert hash was revoked" elif cred_dict['cmd'] == cred_protocol.hex_op('op_revoke_address'): # if address revocation is found stop looking since all other # revocations will be invalid issuer_pkh = P2pkhAddress(issuer_address).to_hash160() if issuer_pkh == cred_dict['data']['pkh']: break # if not revoked but not valid this means that it was expired; now that we # checked for revocations we can show the expiry error if not valid: return False, reason # now that the issuer (anchoring) was validated validate the certificate # with the owner's public key (vpdf v2) # get owner and owner_proof removing the latter owner, owner_proof = get_owner_and_remove_owner_proof(tmp_filename) if owner: # get public key pk = PublicKey.from_hex(owner['pk']) # get file hash sha256_hash = None with open(tmp_filename, 'rb') as pdf: sha256_hash = hashlib.sha256(pdf.read()).hexdigest() # cleanup now that we got original filehash os.remove(tmp_filename) # finally check if owner signature is valid #print(pk.get_address().to_string(), pk.to_hex(), sha256_hash, owner_proof) try: if (pk.verify(owner_proof, sha256_hash)): pass except Exception: #BadSignatureError: return False, 'owner signature could not be validated' # in a valid credential the reason could contain an expiry date return True, reason
def _fill_pdf_metadata(out_file, issuer, issuer_address, column_fields, data, global_columns, verify_issuer, conf, interactive=False): # create version version = 2 # create issuer object (json) issuer = { "name": issuer, "identity": { "address": issuer_address, "verification": json.loads(verify_issuer)['methods'] } } # create metadata object (json) and add metadata metadata = {} # add custom metadata if column_fields: metadata_fields = json.loads(column_fields)['columns'] for f in metadata_fields: key = list(f)[0] if key in data: field_properties = f[key] field_properties['value'] = data[key] metadata[key] = field_properties # add global field metadata if global_columns: global_fields = json.loads(global_columns)['fields'] for g in global_fields: key = list(g)[0] # note that global fields override column data metadata[key] = g[key] # now look at special owner name/pubkey columns explicitly in code # TODO we should probably check if the public key is valid owner = None owner_address = None owner_pk = None if '__OWNER_PK__' in data and data[ '__OWNER_PK__'] and '__OWNER_ADDRESS__' in data and data[ '__OWNER_ADDRESS__']: # TODO maybe just calculate address from public key? owner_address = data['__OWNER_ADDRESS__'] owner_pk = data['__OWNER_PK__'] owner = { "name": data['__OWNER_NAME__'], "owner_address": data['__OWNER_ADDRESS__'], # TODO needed? - can be derived "pk": owner_pk } # add the metadata pdf_metadata = PdfDict(version=version, issuer=json.dumps(issuer), metadata=json.dumps(metadata), owner=json.dumps(owner), owner_proof='', chainpoint_proof='') else: # add the metadata (without dumps(owner) to keep owner empty) pdf_metadata = PdfDict(version=version, issuer=json.dumps(issuer), metadata=json.dumps(metadata), owner='', owner_proof='', chainpoint_proof='') pdf = PdfReader(out_file) if pdf.Info: pdf.Info.update(pdf_metadata) else: pdf.Info = pdf_metadata PdfWriter().write(out_file, pdf) # if owner exists then need to add owner_proof # hash pdf, sign hash message using node and add in owner_proof if owner: ##import time ##start = time.time() sha256_hash = None with open(out_file, 'rb') as pdf: sha256_hash = hashlib.sha256(pdf.read()).hexdigest() if (conf.testnet): setup('testnet') else: setup('mainnet') host, port = conf.full_node_url.split( ':') #TODO: update when NodeProxy accepts full url! proxy = NodeProxy(conf.full_node_rpc_user, conf.full_node_rpc_password, host, port).get_proxy() # Due to an old unresolved issue still pending in Bitcoin v0.20.0 # signmessage does not support signing with bech32 key. # To resolve we use the public key to get the base58check encoding that # signmessage is happy with so that we can sign! if (owner_address.startswith('bc') or owner_address.startswith('tb')): owner_address = PublicKey(owner_pk).get_address().to_string() # NOTE that address (the encoding) might have changed here from bech32 # to legacy... take care if you use it again in this function! sig = proxy.signmessage(owner_address, sha256_hash) # add owner_proof to metadata pdf_metadata = PdfDict(owner_proof=sig) pdf = PdfReader(out_file) pdf.Info.update(pdf_metadata) PdfWriter().write(out_file, pdf) ##end = time.time() ##print(end-start, " seconds") ##exit() if interactive: # print progress print('.', end="", flush=True)
def spend_p2sh_cltv_p2pkh(proxy, block_height, sender_priv_key, p2sh_addr, rec_addr): """ Spends funds from a P2SH address with an absolute locktime to a P2PKH receiver address. :param proxy: JSON RPC proxy for connecting to the network. :param block_height: Block height the lock is valid for. :param sender_priv_key: Private key of the address locking the funds. :param p2sh_addr: P2SH address containing the funds. :param rec_addr: P2PKH address receiving the funds. """ # mine 100 blocks and send bitcoin to the P2SH address proxy.generatetoaddress(100, p2sh_addr) # mine 100 blocks to make mined bitcoin spendable, emulates 'bitcoin-cli -generate 100' for _ in range(100): proxy.generatetoaddress(1, proxy.getnewaddress()) # retrieve unspent UTXOs for the P2SH address p2sh_addr_unspent = proxy.listunspent(0, 99999999, [p2sh_addr]) # create absolute-block locking sequence for the transaction inputs seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, block_height) # create transaction inputs for unspent UTXOs # and calculate the total unspent bitcoins they contain tx_inputs = [] total_unspent = 0 for utxo in p2sh_addr_unspent: tx_inputs.append( TxInput(utxo['txid'], utxo['vout'], sequence=seq.for_input_sequence())) total_unspent += utxo['amount'] print("Unspent bitcoin in address {} : {}".format(p2sh_addr, total_unspent)) # get the public key of the receiving address for the funds to be sent to rec_pub_key = proxy.getaddressinfo(rec_addr)['pubkey'] rec_pk = PublicKey(rec_pub_key) # calculate fee satoshis_per_kb = requests \ .get('https://api.blockcypher.com/v1/btc/test3') \ .json()['medium_fee_per_kb'] # formula: |inputs| * 180 + 34 * |outputs| +- |inputs| tx_size = len(tx_inputs) * 180 + 34 * 1 + 10 + len(tx_inputs) # we calculate fees in terms of bitcoin fee = (tx_size / 1024) * (satoshis_per_kb / 10e8) # create the transaction output tx_output = TxOutput(to_satoshis(Decimal(total_unspent) - Decimal(fee)), rec_pk.get_address().to_script_pub_key()) # set a lock time in blocks for the transaction lock = Locktime(block_height) # create the transaction tx = Transaction(tx_inputs, [tx_output], lock.for_transaction()) unsigned_tx = tx.serialize() print('Raw unsigned transaction:', unsigned_tx) # we need to rebuild the redeem script from the P2SH private key # make the locking P2PKH address private key sender_sk = PrivateKey(sender_priv_key) # retrieve the public key of the locking address sender_pk = sender_sk.get_public_key() # rebuild the redeem script redeem_script = Script([ seq.for_script(), 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', sender_pk.get_address().to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG' ]) # for every input of the transaction for i, txin in enumerate(tx.inputs): # create the signature for redeeming the funds sig = sender_sk.sign_input(tx, i, redeem_script) # and sign the input txin.script_sig = Script( [sig, sender_pk.to_hex(), redeem_script.to_hex()]) signed_tx = tx.serialize() print('Raw signed transaction:', signed_tx) print('Transaction ID:', tx.get_txid()) # verify that the transaction is valid ver = proxy.testmempoolaccept([signed_tx]) if ver[0]['allowed']: # if the transaction is valid send the transaction to the blockchain print('Transaction is valid.') proxy.sendrawtransaction(signed_tx) print('{} Bitcoin sent to address {}'.format( Decimal(total_unspent) - Decimal(fee), rec_addr)) else: # otherwise, display the reason the transaction failed print('Transaction rejected. Reason:', ver[0]['reject-reason'])
def test_pubkey_to_hash160(self): pub = PublicKey(self.public_key_hex) self.assertEqual(pub.get_address().to_hash160(), pub.to_hash160())
def load_data_json(priv=False, pub=False, pub_hash=False, timelock_csv=False, timelock_tx=False, timelock_log=False, p2pk=False): """loads parameters used to create P2SH address and spending transactions from the data.json file - initiates/converts applicable objects/data returns : dict of data for the Attributes specified - e.g {'priv_key': object, 'pub': object, 'pub_hash': 'string'} """ # reads user data from data.json with open('data.json', 'r') as json_file: data = json.load(json_file) usr_inputs = data['csv_timelock']['inputs'] # initiates key objects if priv or pub or pub_hash: usr_priv = usr_inputs['priv_key'] usr_pub = usr_inputs['pub_key'] if priv and not usr_priv: errors.missing_data('private key') elif usr_priv: priv_key = PrivateKey.from_wif(usr_priv) pub_key = priv_key.get_public_key() elif usr_pub: pub_key = PublicKey.from_hex(usr_pub) else: # prints error msg & raises systemExit errors.missing_data('public key') # loads timelock condition and converts format if timelock_csv or timelock_tx or timelock_log: lock_height = usr_inputs['block_lock'] if lock_height: seq = Sequence(TYPE_RELATIVE_TIMELOCK, lock_height) timelock_script = seq.for_script() timelock_txin = seq.for_input_sequence() else: # prints error msg & raises systemExit errors.missing_data('timelock period') # initaites P2pkhAddress object if p2pk: address = usr_inputs['p2pkh_address'] if address: p2pkh_address = P2pkhAddress.from_address(address) else: # prints error msg & raises systemExit errors.missing_data('P2PKH address') # fills return outputs with specified data from func arguments outputs = {} if priv: outputs['priv_key'] = priv_key if pub: outputs['pub_key'] = pub_key if pub_hash: outputs['pub_key_hash'] = pub_key.to_hash160() if timelock_csv: outputs['timelock_csv'] = timelock_script if timelock_tx: outputs['timelock_tx'] = timelock_txin if timelock_log: outputs['timelock_log'] = lock_height if p2pk: outputs['p2pk_addr'] = p2pkh_address return outputs
def test_get_uncompressed_address(self): pub = PublicKey(self.public_key_hex) self.assertEqual( pub.get_address(compressed=False).to_string(), self.address)
def test_pubkey_uncompressed_from_hex(self): pub = PublicKey.from_hex(self.public_key_hex) self.assertEqual(pub.to_hex(compressed=False), self.public_key_hex)
def test_pubkey_creation(self): pub1 = PublicKey(self.public_key_hex) self.assertEqual(pub1.to_bytes(), self.public_key_bytes) pub2 = PublicKey(self.public_key_hexc) self.assertEqual(pub2.to_bytes(), self.public_key_bytes)
def test_verify_external(self): self.assertTrue( PublicKey.verify_message(self.external_address, self.external_signature, self.message))
def test_sign_and_verify(self): signature = self.priv.sign_message(self.message) self.assertEqual(signature, self.deterministic_signature) self.assertTrue( PublicKey.verify_message(self.address, signature, self.message))