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 main(): # always remember to setup the network setup('testnet') # get a node proxy using default host and port proxy = NodeProxy('rpcuser', 'rpcpw').get_proxy() # call the node's getblockcount JSON-RPC method count = proxy.getblockcount() # call the node's getblockhash JSON-RPC method block_hash = proxy.getblockhash(count) # call the node's getblock JSON-RPC method and print result block = proxy.getblock(block_hash) print(block) # print only the difficulty of the network print(block['difficulty'])
def issue_op_return(conf, op_return_bstring, interactive=False): op_return_hex = binascii.hexlify(op_return_bstring).decode() if interactive: print('\nConfigured values are:\n') print('working_directory:\t{}'.format(conf.working_directory)) print('issuing_address:\t{}'.format(conf.issuing_address)) print('full_node_url:\t\t{}'.format(conf.full_node_url)) print('full_node_rpc_user:\t{}'.format(conf.full_node_rpc_user)) print('testnet:\t\t{}'.format(conf.testnet)) print('tx_fee_per_byte:\t{}'.format(conf.tx_fee_per_byte)) print('Bytes for OP_RETURN:\n{}'.format(op_return_bstring)) print('Hex for OP_RETURN:\n{}'.format(op_return_hex)) op_return_cert_protocol = op_return_hex if interactive: consent = input('Do you want to continue? [y/N]: ').lower() in ('y', 'yes') if not consent: sys.exit() # test explicitly when non interactive if not conf.full_node_rpc_password and interactive: conf.full_node_rpc_password = getpass.getpass('\nPlease enter the password for the node\'s RPC user: '******'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() # create transaction tx_outputs = [] unspent = sorted(proxy.listunspent(1, 9999999, [conf.issuing_address]), key=lambda x: x['amount'], reverse=False) if not unspent: raise ValueError("No UTXOs found") issuing_pubkey = proxy.getaddressinfo(conf.issuing_address)['pubkey'] tx = None tx_inputs = [] inputs_amount = 0 # coin selection: use smallest UTXO and if not enough satoshis add next # smallest, etc. until sufficient tx fees are accumulated # TODO wrt dust instead of adding another UTXO we should just remove the # change_output and allocate the remaining (<546sats) to fees for utxo in unspent: txin = TxInput(utxo['txid'], utxo['vout']) tx_inputs.append(txin) inputs_amount += utxo['amount'] change_script_out = P2pkhAddress(conf.issuing_address).to_script_pub_key() change_output = TxOutput(inputs_amount, change_script_out) op_return_output = TxOutput(0, Script(['OP_RETURN', op_return_cert_protocol])) tx_outputs = [ change_output, op_return_output ] tx = Transaction(tx_inputs, tx_outputs) # sign transaction to get its size r = proxy.signrawtransactionwithwallet(tx.serialize()) if r['complete'] == None: if interactive: sys.exit("Transaction couldn't be signed by node") else: raise RuntimeError("Transaction couldn't be signed by node") signed_tx = r['hex'] signed_tx_size = len(signed_tx) # calculate fees and change tx_fee = (signed_tx_size // 2 + 1) * conf.tx_fee_per_byte # TODO number_to_decimal8 is temporary here.. should used bitcoin # library instead change_amount = inputs_amount - number_to_decimal8(tx_fee / 100000000) # the default Bitcoin Core node doesn't allow the creation of dust UTXOs # https://bitcoin.stackexchange.com/questions/10986/what-is-meant-by-bitcoin-dust # if change is less than 546 satoshis that is considered dust (with the # default node parameters) then include another UTXO if change_amount >= 0.00000550: break if(change_amount < 0): if interactive: sys.exit("Specified address cannot cover the transaction fee of: {} satoshis".format(tx_fee)) else: raise RuntimeError("insufficient satoshis, cannot create transaction") # update tx out for change and re-sign tx.outputs[0].amount = change_amount r = proxy.signrawtransactionwithwallet(tx.serialize()) if r['complete'] == None: if interactive: sys.exit("Transaction couldn't be signed by node") else: raise RuntimeError("Transaction couldn't be signed by node") signed_tx = r['hex'] # send transaction if interactive: print('The fee will be {} satoshis.\n'.format(tx_fee)) consent = input('Do you want to issue on the blockchain? [y/N]: ').lower() in ('y', 'yes') if not consent: sys.exit() tx_id = proxy.sendrawtransaction(signed_tx) return tx_id
def get_connection(): global proxy proxy = NodeProxy(rpcuser='******', rpcpassword='******', port="18443").get_proxy()
# # Enter private key and bitcoin node username and password here # USERNAME = '******' PASSWORD_HASH = 'ti8KMfu1Afeh8UZYYN32Lqo5NfBIN6tC-pnSW-OBp7A=' PRIVATE_KEY = 'cSyZjejfhK5gaVYoG9pgfdMrZzw7rXufpiG4oDaYShanJhwpqGcE' BLOCKHEIGHT = 1747851 # # Connect to testnet # setup( 'testnet' ) proxy = NodeProxy( USERNAME, PASSWORD_HASH ).get_proxy() # # 1) accept a future time, expressed either in block height or in UNIX Epoch time, # and a private key (to recreate the redeem script as above and also use to unlock the P2PKH part) # from_privkey = PrivateKey( 'cSyZjejfhK5gaVYoG9pgfdMrZzw7rXufpiG4oDaYShanJhwpqGcE' ) from_pubkey = from_privkey.get_public_key() from_address = from_pubkey.get_address() from_hash = from_address.to_hash160() redeem_script = Script( [BLOCKHEIGHT, 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', from_hash, 'OP_EQUALVERIFY', 'OP_CHECKSIG'] ) # # 2) accept a P2SH address to get the funds from (the one created by the first script)
def buy_lambo(key, lock, from_addr, to_addr): setup(os.getenv("BUY_LAMBO_BTC_NET", "regtest")) # Create a proxy to JSON-RPC api chain = NodeProxy(os.getenv("BUY_LAMBO_RPC_USER", "rpcuser"), os.getenv("BUY_LAMBO_RPC_PASSWORD", "rpcpassword")).get_proxy() # Try executing a command to see if node is running try: chain.getblockcount() except Exception: print("Error: Node isn't working!") return # Check addresses if not chain.validateaddress(from_addr)["isvalid"]: print("Error: `from_addr` is not a valid address!") return if not chain.validateaddress(to_addr)["isvalid"]: print("Error: `to_addr` is not a valid address!") return # Set lock sequence prefix seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, lock) # Get public key (found from private key) private_key = PrivateKey(key) public_key = private_key.get_public_key() # Add address to wallet so we can get utxos from bitcoin core chain.importaddress(from_addr) # Get UTXOs utxos = chain.listunspent(1, 9999999, [from_addr]) # Create inputs txin = [] total_btc = Decimal(0) for utxo in utxos: # Note that for each input we set the correct nSequence txin.append( TxInput(utxo["txid"], utxo["vout"], sequence=seq.for_input_sequence())) total_btc = total_btc + utxo["amount"] if total_btc == 0: return print("\nThere aren't any UTXOs :(.") # Create a fee-less output txout = TxOutput(total_btc - Decimal(0.1), P2pkhAddress(to_addr).to_script_pub_key()) # Create dummy transaction (to calculate size) tx = Transaction([TxInput.copy(inpt) for inpt in txin], [txout], locktime=Locktime(lock).for_transaction()) # create the redeem script - needed to sign the transaction redeem_script = Script([ seq.for_script(), "OP_CHECKLOCKTIMEVERIFY", "OP_DROP", "OP_DUP", "OP_HASH160", public_key.get_address().to_hash160(), "OP_EQUALVERIFY", "OP_CHECKSIG", ]) # use the private key corresponding to the address that contains the # UTXO we are trying to spend to create the signature for the txin - # note that the redeem script is passed to replace the scriptSig for ind, txinput in enumerate(tx.inputs): sig = private_key.sign_input(tx, ind, redeem_script) txinput.script_sig = Script( [sig, public_key.to_hex(), redeem_script.to_hex()]) # Calculate fees sat_per_byte = requests.get( "https://bitcoinfees.earn.com/api/v1/fees/recommended").json( )["fastestFee"] fee = Decimal( max((len(bytes.fromhex(tx.serialize())) * sat_per_byte) / SATOSHIS_PER_BITCOIN, 0)) if fee == 0: print("WARNING: There isn't enough balance to calculate fees.") # Create final tx with correct txout value txout = TxOutput(total_btc - fee, P2pkhAddress(to_addr).to_script_pub_key()) tx = Transaction([TxInput.copy(inpt) for inpt in tx.inputs], [txout], locktime=Locktime(lock).for_transaction()) for ind, txinput in enumerate(tx.inputs): sig = private_key.sign_input(tx, ind, redeem_script) txinput.script_sig = Script( [sig, public_key.to_hex(), redeem_script.to_hex()]) signed_tx = tx.serialize() # print raw transaction print("\nRaw unsigned transaction:\n{}".format( Transaction(txin, [txout]).serialize())) # print raw signed transaction ready to be broadcasted print("\nRaw signed transaction:\n{}".format(signed_tx)) print("\nTxId: {}".format(tx.get_txid())) # Check if is valid and send it if chain.testmempoolaccept([signed_tx])[0]["allowed"]: chain.sendrawtransaction(signed_tx) print("\nSent to Block-chain! Have fun with those {} BTC.".format( total_btc - fee)) else: print("\nCan't send (yet)!") print("Reason: `{}`".format( chain.testmempoolaccept([signed_tx])[0]["reject-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 main(): #Connect to the regtest network setup('regtest') proxy = NodeProxy('user', 'bitcoin').get_proxy() #Accept absolute future block amount User current_blocks = proxy.getblockcount() absolute_blocks = int( input( f'\nPlease input the future block height. The current block height is: {current_blocks}.\nIt must match the one in the redeem script from which the P2SH address (with the locked funds) was derived\n' )) #Accept the Private Key from User sk = PrivateKey( input( '\nPlease enter your Private Key (used to create the address from which the P2SH Address was derived)\n' )) #Accept the P2SH Address with the funds p2sh_addr = input( '\nPlease enter the P2SH address which has the locked funds\n') #Import the address into the wallet proxy.importaddress(f'{p2sh_addr}') print( f'\nThe P2SH address {p2sh_addr} has been imported into the wallet.\n') #Get all UTXOs for this address all_utxos = proxy.listunspent(1, 9999999, [f'{p2sh_addr}']) print(f'\nAll the UTXO Objects for this address are:\n{all_utxos}\n') #Calculate total funds available. Aggregate all UTXO amounts. def totalFunds(): total = Decimal(0) for utxo in all_utxos: total += Decimal(utxo["amount"]) return total total_funds = totalFunds() print("Total funds available: ", total_funds) #Instantiate the timelock sequence seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, absolute_blocks) #Create an array of inputs from these UTXOs def getInputs(): inputs = [] count = 0 for utxo in all_utxos: #create inputs and append them into an array #First, create an input utxo = all_utxos[count] txin = TxInput(utxo["txid"], utxo["vout"], sequence=seq.for_input_sequence()) #then, append it to the array inputs.append(txin) ++count return inputs inputs = getInputs() print(f'The inputs created from these outputs are:\n {inputs}') #Use User's Secret Key (Accepted above) to recreate the Public Key pk = sk.get_public_key() #We recreate the P2PKH Addr to recreate the other half of the redeemScript p2pkh_addr = pk.get_address() #We recreate the redeem script redeem_script = Script([ absolute_blocks, 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', p2pkh_addr.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG' ]) #Confirm that the P2SH address is the same one to which funds were sent by receating the P2SH from this redeem script addr = P2shAddress.from_script(redeem_script).to_string() print( f'\nCheck if this is the address with the locked funds:\n{addr} vs what you input above {p2sh_addr}' ) #Accept the receiving P2PKH from User destination_addr = P2pkhAddress( input( '\nPlease enter the address you want to send the locked funds to:\n' )) #Calculate txn size to estimate fee from inputs only so we subtract it from funds tx_test = Transaction(inputs, [], Locktime(absolute_blocks).for_transaction()) tx_size = tx_test.get_size() # 0.0001 is an appropriate fee per byte so we use that est_tx_fee = tx_size / 1000 * 0.0001 #Create a txout transferring total locked funds (minus estimated fees). txout = TxOutput(to_satoshis(total_funds - Decimal(est_tx_fee)), destination_addr.to_script_pub_key()) #Create Txn. Passing in the inputs, output, and the future absolute lock time tx = Transaction(inputs, [txout], Locktime(absolute_blocks).for_transaction()) #Print the newly created txn print(f'\nThe raw unsigned txn is: {tx.serialize()}\n') #Calulate the Appropriate Transaction Fee According to Txn Size. #First, get the fee estimate from network. Useful only for mainnet and testnet. def getFee(): fee = proxy.estimatesmartfee(5, "ECONOMICAL") if fee["errors"]: print( 'Network data not enough to calculate fee. Setting default fee: 0.0001 BTC' ) return 0.0001 else: return fee["feerate"] #Then, calculate the size of our Txn and then multiply it by the per-byte fee est_fee = getFee() #per byte tx_size = tx.get_size() print(f'\nThe transaction size is:\n{tx_size} bytes') tx_fee = tx_size / 1000 * est_fee print(f'\nThe recommended fee for this transaction is:\n{tx_fee}') #Need to sign all inputs def signInputs(): input_index = 0 for input in inputs: #Use the Secret Key corresponding to the P2SH to create the signature for the txn/ sign all inputs #Redeem script is passed to replace the scriptSig inputSig = sk.sign_input(tx, input_index, redeem_script) input.script_sig = Script( [inputSig, pk.to_hex(), redeem_script.to_hex()]) ++input_index signInputs() signed_tx = tx.serialize() #Print the signed raw txn, ready to be checked for validity print(f"\nRaw signed transaction:\n{signed_tx}") print( f"\nTransaction ready to be broadcast. Transaction Id: {tx.get_txid()}" ) #Test for validity isValid = proxy.testmempoolaccept([f'{signed_tx}']) print(f'\nTransaction validity check result:\n{isValid}') #Broadcast txn #needs signed_tx if isValid[0]["allowed"]: if input('\nSend transaction to network? Y / N: ') == 'Y': sent_tx_id = proxy.sendrawtransaction(f'{signed_tx}') print( f'\n***The transaction with id {sent_tx_id} has been sent to the network***' ) else: print( f'\nUser decided not to send the funds and exited the program.' ) exit() else: reason = isValid[0]["reject-reason"] print(f'\nThe signed raw transaction is invalid. Reason: {reason}')
def __init__(self): setup('testnet') self.proxy = NodeProxy('bitcoinrpc', '123456').get_proxy()
class Bitcoin(): def __init__(self): setup('testnet') self.proxy = NodeProxy('bitcoinrpc', '123456').get_proxy() # ============================================================================================================== # Encode the challenge data into a bitcoin transaction. # The function takes as input the publickey of the paying address, # challenge ciphertext and user private key(in the conf file) # ============================================================================================================== def tx_encf(self, vk_p, ct_c, user_conf_file_path): user_conf_file = open(user_conf_file_path) conf = json.load(user_conf_file) sk_u = conf['SK'] sk_u = PrivateKey(secret_exponent=int(sk_u, 16)) vk_u = sk_u.get_public_key() addr_u = vk_u.get_address().to_string() #txid = '826711590bb543028418d312cf2229eba1b4fbf295fa73dbc37654085e1fc925' #txid = '49ab8b8f6913d5fb97c067fe9466710f62e195bab5366e484d1b2334a5b5c0c8' #tx_index = 1 txid = input( "Fund the address: {0} \nand insert the txid:\n".format(addr_u)) tx_index = input("tx_index:\n") print("txid: ", txid) print("tx_index: ", tx_index) signed_tx_hex = self._tx_encf('04' + vk_p, ct_c, sk_u, txid, int(tx_index)) #tx_hash = "88e602cc4766edd442ee99270f2e94bf6f7625a9b49721311fc9bba4dfe8defa" tx_hash = self.proxy.sendrawtransaction(signed_tx_hex) return tx_hash 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 # ================================================================================================ # This function given a tx hash, decodes it and returns the user's public key and ciphertext # ================================================================================================ 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 # ================================================================================================ # Encode the response data into a bitcoin transaction. # The function takes as input the private key of the paying key (derived from the shared key) # and the response data (ciphertext) # ================================================================================================ def tx_encb(self, sk_s, ct_r, txid): sk_s = PrivateKey(secret_exponent=int(sk_s, 16)) signed_tx_hex = self._tx_encb(sk_s, ct_r, txid, txin_index=0) #tx_hash = "33f4e564128ab824fe5864cc281ee16cb4f58db502a682fb7dca07bf36717b11" tx_hash = self.proxy.sendrawtransaction(signed_tx_hex) return tx_hash def _tx_encb(self, sk_s, ct_r, txin_id, txin_index): # create transaction input from tx id of UTXO txin = TxInput(txin_id, txin_index) # create an output where the address is the ciphertext[0:20] cipher1_txout = TxOutput( 0.000001, Script([ 'OP_DUP', 'OP_HASH160', ct_r[:40], 'OP_EQUALVERIFY', 'OP_CHECKSIG' ])) # create another output where the address is the ciphertext[20:40] cipher2_txout = TxOutput(0.000001, Script(['OP_HASH160', ct_r[40:], 'OP_EQUAL'])) # create transaction from inputs/outputs -- default locktime is used tx = Transaction([txin], [cipher1_txout, cipher2_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_p = sk_s.get_public_key() addr_p = vk_p.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_p) sig = sk_s.sign_input(tx, 0, from_addr.to_script_pub_key()) # set the scriptSig (unlocking script) vk_p = vk_p.to_hex() txin.script_sig = Script([sig, vk_p]) signed_tx = tx.serialize() print("\nRaw signed transaction:\n" + signed_tx) return signed_tx # ================================================================================================ # Given a block id and an public key of transaction return the encoded data in that transaction # ================================================================================================ 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 # ================================================================================================ # Given a block id return the hashes of the transactions in that block. # ================================================================================================ def get_block_txs(self, block_id): block_hash = self.proxy.getblockhash(block_id) block_info = self.proxy.getblock(block_hash) txs_hashes = block_info[ 'tx'] # Get the hash of transactions inside this block return txs_hashes
if __name__ == '__main__': # default arguments height = 10 pubk = None # retrieve the arguments try: height = int(sys.argv[1]) pubk = sys.argv[2] except IndexError: # ignore missing arguments pass # set up the environment for regtest setup('regtest') username, password = '******', 'pass' wallet = '~/.bitcoin/regtest/wallets/mywallet' # set up a JSON RPC proxy rpc_proxy = NodeProxy(username, password).get_proxy() # load wallet try: rpc_proxy.loadwallet(wallet) except JSONRPCException: # wallet already loaded pass # run the script create_p2sh(rpc_proxy, height, pubk)
from bitcoinutils.script import Script # # Enter private key and bitcoin node username and password here # USERNAME = '******' PASSWORD_HASH = 'ti8KMfu1Afeh8UZYYN32Lqo5NfBIN6tC-pnSW-OBp7A=' PRIVATE_KEY = 'cSyZjejfhK5gaVYoG9pgfdMrZzw7rXufpiG4oDaYShanJhwpqGcE' # # Connect to testnet # setup('testnet') proxy = NodeProxy(USERNAME, PASSWORD_HASH).get_proxy() # # 1) accept a public (or optionally a private) key for the P2PKH part of the redeem script # privkey = PrivateKey(PRIVATE_KEY) pubkey = privkey.get_public_key() # # 2) accept a future time expressed either in block height or in UNIX Epoch time # blockheight = proxy.getblockcount() + 1 print("future time in absolute block height: " + str(blockheight))
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 private key, a future time expressed either in block height or in UNIX Epoch time and ' 'the P2SH address to send the funds') parser.add_argument('key', help="Add the private key.") parser.add_argument( '-param', type=int, help="Add the number of blocks or the time expressed in seconds.") parser.add_argument('-to_address', type=str, help="Add the adress that will sent/spend") args = parser.parse_args() # set values key = args.key absolute_param = args.param send_address = args.to_address # set Locktime seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, absolute_param) locktime = Locktime(absolute_param) # set proxy username = "******" password = "******" proxy = NodeProxy(username, password).get_proxy() # secret key corresponding to the pubkey needed for the P2SH (P2PKH) transaction p2pkh_sk = PrivateKey(key) p2pkh_pk = p2pkh_sk.get_public_key() # get the address (from the public key) p2pkh_addr = p2pkh_pk.get_address() # print("Private key: " + p2pkh_sk. to_wif(compressed=True)) # 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' ]) # accept a P2SH address to get the funds from addr = P2shAddress.from_script(redeem_script) print("The P2SH address to get the funds from is : " + addr.to_string()) # check if the P2SH address has any UTXOs to get funds from proxy.importaddress(addr.to_string(), "P2SH to get the funds from") minconf = 0 maxconf = 99999999 my_list = proxy.listunspent(minconf, maxconf, [addr.to_string()]) # Gather all funds that the P2SH address received to send to the P2PKH address provided txin_list = [] btc_to_send = 0 for i in my_list: txin = TxInput(i['txid'], i['vout'], sequence=seq.for_input_sequence()) txin_list.append(txin) btc_to_send = btc_to_send + i['amount'] if btc_to_send == 0: print("No btc found to send") quit() # accept a P2PKH address to send the funds to to_addr = P2pkhAddress(send_address) print("The P2PKH address to send the funds to is : " + to_addr.to_string()) # calculate the appropriate fees with respect to the size of the transaction response = requests.get("https://mempool.space/api/v1/fees/recommended") fee_per_byte = response.json()['fastestFee'] print("Fastest fee per byte is : %d " % fee_per_byte) # calculate transaction size as described at # https://bitcoin.stackexchange.com/questions/1195/how-to-calculate-transaction-size-before-sending-legacy-non-segwit-p2pkh-p2sh tx_size = len(my_list) * 180 + 34 + 10 + len(my_list) total_fees = tx_size * fee_per_byte / (1024 * 10**8) print('Total fees are : ', total_fees) # Calculate the final amount amount = btc_to_send - total_fees # print(amount) # Create transaction output txout = TxOutput(to_satoshis(amount), to_addr.to_script_pub_key()) # Create transaction after the inputs and the outputs tx = Transaction(txin_list, [txout], locktime.for_transaction()) # For each transaction - when dealing with multiple inputs, you will need to sign all of them for i, txin in enumerate(my_list): sig = p2pkh_sk.sign_input(tx, i, redeem_script) # print(sig) # set the scriptSig (unlocking script) -- unlock the P2PKH (sig, pk) plus # the redeem script, since it is a P2SH txin.script_sig = Script( [sig, p2pkh_pk.to_hex(), redeem_script.to_hex()]) # display the raw signed transaction, ready to be broadcasted signed_tx = tx.serialize() print("\nRaw signed transaction:\n" + signed_tx) # display the raw unsigned transaction print("\nRaw unsigned transaction:\n" + tx.serialize()) # display the transaction id print("\nTxId:", tx.get_txid()) # verify that the transaction is valid and will be accepted by the Bitcoin nodes # if the transaction is valid, send it to the blockchain is_valid = proxy.testmempoolaccept([signed_tx]) # print(is_valid) if is_valid[0]['allowed']: print("Transaction is valid!") print("Sending transaction to blockchain..") proxy.sendrawtransaction(signed_tx) else: print("Transaction not valid") quit()
def main(): # always remember to setup the network setup('regtest') # RPC credentials for communicating with the node rpcuser = '******' rpcpass = '******' proxy = NodeProxy(rpcuser, rpcpass).get_proxy() # set values block_height = 140 # secret key needed to spend P2PKH that is wrapped by P2SH p2pkh_sk = PrivateKey( 'cSbKZh6a6wNUAQ8pr2KLKeZCQ4eJnFmN35wtReaoU4kCP97XQu6W') # this is the P2SH address the funds have been locked in p2shaddress = '2NGWStpuXtke1VXCTgNnzUgWbun7eY2f3nH' # this is the address the funds will be sent to to_addr = P2pkhAddress('mnS1ng5D1hdvLkYAK2oS8R1C4e37aQdVoC') seq = Sequence(TYPE_ABSOLUTE_TIMELOCK, block_height) lock = Locktime(block_height) # import the address as watch-only print('importaddress') proxy.importaddress(p2shaddress, "P2SH absolute timelock", True) # find all UTXOs for this address. 10.000.000 should be enough print('listunspent') list_unspent = proxy.listunspent(0, 9999999, [p2shaddress]) # create transaction inputs for all UTXOs. Calculate the total amount of # bitcoins they contain txin_list = [] total_amount = 0 for i in list_unspent: txin = TxInput(i['txid'], i['vout'], sequence=seq.for_input_sequence()) txin_list.append(txin) total_amount = total_amount + i['amount'] if total_amount == 0: print("No funds to move") sys.exit(0) # derive public key and adddress from the private key p2pkh_pk = p2pkh_sk.get_public_key().to_hex() p2pkh_addr = p2pkh_sk.get_public_key().get_address() # create the redeem script - needed to sign the transaction redeem_script = Script([ seq.for_script(), 'OP_CHECKLOCKTIMEVERIFY', 'OP_DROP', 'OP_DUP', 'OP_HASH160', p2pkh_addr.to_hash160(), 'OP_EQUALVERIFY', 'OP_CHECKSIG' ]) # get fees using API. Although we may be running in regtest, we'll use the # fees as if we were using testnet (fees are in satoshis) url = 'https://api.blockcypher.com/v1/btc/test3' resp = requests.get(url) fee_per_kb = resp.json()['medium_fee_per_kb'] # calculate transaction size according to: # in*180 + out*34 + 10 plus or minus 'in' # https://bitcoin.stackexchange.com/questions/1195/how-to-calculate-transaction-size-before-sending-legacy-non-segwit-p2pkh-p2sh # we'll play it safe and use the upper bound tx_size = len(txin_list) * 180 + 34 + 10 + len(txin_list) fees = tx_size * fee_per_kb / (1024 * 10**8) print('fees:', fees) # create the output txout = TxOutput( Decimal(total_amount) - Decimal(fees), to_addr.to_script_pub_key()) # create transaction from inputs/outputs tx = Transaction(txin_list, [txout], lock.for_transaction()) # use the private key corresponding to the address that contains the # UTXO we are trying to spend to create the signatures for all txins - # note that the redeem script is passed to replace the scriptSig for i, txin in enumerate(txin_list): sig = p2pkh_sk.sign_input(tx, i, redeem_script) # set the scriptSig (unlocking script) -- unlock the P2PKH (sig, pk) plus # the redeem script, since it is a P2SH txin.script_sig = Script([sig, p2pkh_pk, redeem_script.to_hex()]) # serialize the transaction signed_tx = tx.serialize() # test if the transaction will be accepted by the mempool print('testmempoolaccept') res = proxy.testmempoolaccept([signed_tx]) print(res) if not res[0]['allowed']: print("Transaction not valid") sys.exit(1) # print raw transaction print("\nRaw unsigned transaction:\n" + tx.serialize()) # print raw signed transaction ready to be broadcasted print("\nRaw signed transaction:\n" + signed_tx) print("\nTxId:", tx.get_txid())
def issue_op_return(conf, op_return_bstring, interactive=False): op_return_hex = binascii.hexlify(op_return_bstring).decode() if interactive: print('\nConfigured values are:\n') print('working_directory:\t{}'.format(conf.working_directory)) print('issuing_address:\t{}'.format(conf.issuing_address)) print('full_node_url:\t\t{}'.format(conf.full_node_url)) print('full_node_rpc_user:\t{}'.format(conf.full_node_rpc_user)) print('testnet:\t\t{}'.format(conf.testnet)) print('tx_fee_per_byte:\t{}'.format(conf.tx_fee_per_byte)) print('Bytes for OP_RETURN:\n{}'.format(op_return_bstring)) print('Hex for OP_RETURN:\n{}'.format(op_return_hex)) op_return_cert_protocol = op_return_hex if interactive: consent = input('Do you want to continue? [y/N]: ').lower() in ('y', 'yes') if not consent: sys.exit() # test explicitly when non interactive if interactive: conf.full_node_rpc_password = getpass.getpass( '\nPlease enter the password for the node\'s RPC user: '******'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() # create transaction tx_outputs = [] unspent = sorted(proxy.listunspent(1, 9999999, [conf.issuing_address]), key=lambda x: hash(x['amount']), reverse=True) issuing_pubkey = proxy.getaddressinfo(conf.issuing_address)['pubkey'] tx_inputs = [TxInput(unspent[0]['txid'], unspent[0]['vout'])] input_amount = unspent[0]['amount'] change_script_out = P2pkhAddress(conf.issuing_address).to_script_pub_key() change_output = TxOutput(input_amount, change_script_out) op_return_output = TxOutput(0, Script(['OP_RETURN', op_return_cert_protocol])) tx_outputs = [change_output, op_return_output] tx = Transaction(tx_inputs, tx_outputs) # sign transaction to get its size r = proxy.signrawtransactionwithwallet(tx.serialize()) if r['complete'] == None: if interactive: sys.exit("Transaction couldn't be signed by node") else: raise RuntimeError("Transaction couldn't be signed by node") signed_tx = r['hex'] signed_tx_size = len(signed_tx) # calculate fees and change tx_fee = (signed_tx_size // 2 + 1) * conf.tx_fee_per_byte # note results is sometimes in e- notation change_amount = input_amount - (tx_fee / 100000000) if (change_amount < 0): if interactive: sys.exit( "Specified address cannot cover the transaction fee of: {} satoshis" .format(tx_fee)) else: raise RuntimeError( "insufficient satoshis, cannot create transaction") # update tx out for change and re-sign tx.outputs[0].amount = change_amount r = proxy.signrawtransactionwithwallet(tx.serialize()) if r['complete'] == None: if interactive: sys.exit("Transaction couldn't be signed by node") else: raise RuntimeError("Transaction couldn't be signed by node") signed_tx = r['hex'] # send transaction if interactive: print('The fee will be {} satoshis.\n'.format(tx_fee)) consent = input('Do you want to issue on the blockchain? [y/N]: ' ).lower() in ('y', 'yes') if not consent: sys.exit() tx_id = proxy.sendrawtransaction(signed_tx) return tx_id