def getrawtransaction(self, txid, verbose=False): """Return transaction with hash txid Raises IndexError if transaction not found. verbse - If true a dict is returned instead with additional information on the transaction. Note that if all txouts are spent and the transaction index is not enabled the transaction may not be available. """ try: r = self._call('getrawtransaction', b2lx(txid), 1 if verbose else 0) except JSONRPCException as ex: raise IndexError('%s.getrawtransaction(): %s (%d)' % (self.__class__.__name__, ex.error['message'], ex.error['code'])) if verbose: r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) del r['hex'] del r['txid'] del r['version'] del r['locktime'] del r['vin'] del r['vout'] r['blockhash'] = lx(r['blockhash']) if 'blockhash' in r else None else: r = CTransaction.deserialize(unhexlify(r)) return r
def calculate_tx_fees(coins, currency, tx_hex): #Process source TX. rpc = coins[currency]["rpc"]["sock"] tx = CTransaction.deserialize(binascii.unhexlify(tx_hex)) #Tally input coins. input_total = decimal.Decimal(0) for vin in tx.vin: txid = b2lx(vin.prevout.hash) vin_tx_hex = rpc.getrawtransaction(txid) vin_tx = CTransaction.deserialize(binascii.unhexlify(vin_tx_hex)) input_value = vin_tx.vout[vin.prevout.n].nValue input_total += decimal.Decimal(str_money_value(input_value)) #Tally output coins. output_total = decimal.Decimal(0) for vout in tx.vout: output_value = decimal.Decimal(str_money_value(vout.nValue)) output_total += output_value #TX fees are the difference between the source and fees = input_total - output_total #Return totals and fees. return [input_total, output_total, fees]
def getrawtransaction(self, txid, verbose=False): """Return transaction with hash txid Raises IndexError if transaction not found. verbose - If true a dict is returned instead with additional information on the transaction. Note that if all txouts are spent and the transaction index is not enabled the transaction may not be available. """ try: r = self._call("getrawtransaction", b2lx(txid), 1 if verbose else 0) except JSONRPCError as ex: raise IndexError( "%s.getrawtransaction(): %s (%d)" % (self.__class__.__name__, ex.error["message"], ex.error["code"]) ) if verbose: r["tx"] = CTransaction.deserialize(unhexlify(r["hex"])) del r["hex"] del r["txid"] del r["version"] del r["locktime"] del r["vin"] del r["vout"] r["blockhash"] = lx(r["blockhash"]) if "blockhash" in r else None else: r = CTransaction.deserialize(unhexlify(r)) return r
def add_input(self, serial_id: int, prevtx: str, prevtx_vout: int, script_sig: str, sequence: int, privkey: str = None) -> None: # the dummy runner sends empty info, skip if len(prevtx) == 0: return # Find the txid of the transaction prev_tx = CTransaction.deserialize(bytes.fromhex(prevtx)) txin = CTxIn(COutPoint(prev_tx.GetTxid(), prevtx_vout), nSequence=sequence) # Get the previous output for its outscript + value prev_vout = prev_tx.vout[prevtx_vout] self.inputs.append({'input': txin, 'serial_id': serial_id, 'sats': prev_vout.nValue, 'prev_outscript': prev_vout.scriptPubKey.hex(), 'redeemscript': script_sig, 'privkey': privkey, })
def refund_tx(self, payer_sig, serial_tx, redeem_script): ''' Sends a transaction refunding the funder of the P2SH address. ''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Set script sig txin.scriptSig = CScript([payer_sig + '\x01', OP_FALSE, redeem_script]) # Verify script redeem_script = CScript(redeem_script) VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) serial_tx = tx.serialize() if not self.test: # txid = self.self.proxy.sendrawtransaction(tx) txid = b2lx(Hash(serial_tx)) else: txid = b2lx(Hash(serial_tx)) self.logger.info("refund_tx: TXID is %s", txid) self.logger.info("refund_tx: RAW TX is %s", b2x(serial_tx)) return serial_tx
def check_refund_works(self, tx_hex, owner_first_sig, owner_second_sig, recipient_sig, actor): global error_log_path try: tx = CTransaction.deserialize(binascii.unhexlify(tx_hex)) redeem_script = bond_redeem_script(self.ecdsa_us, self.ecdsa_them, self.factory.ecdsa_arbiters[0], actor) redeem_script_hash160 = hash160_script(redeem_script) print(tx_hex) print(redeem_script) tx.vin[0].scriptSig = CScript([OP_0, owner_first_sig, owner_second_sig, recipient_sig, redeem_script["bin"]]) p2sh_script_pub_key = CScript([OP_HASH160, redeem_script_hash160["bin"], OP_EQUAL]) print(redeem_script_hash160) VerifyScript(tx.vin[0].scriptSig, p2sh_script_pub_key, tx, 0, (SCRIPT_VERIFY_P2SH,)) signed_tx_hex = b2x(tx.serialize()) return { "tx_hex": signed_tx_hex, "txid": calculate_txid(signed_tx_hex) } except Exception as e: error = parse_exception(e) log_exception(error_log_path, error) print(error) print("Check refund failed.") return None
def provide_transaction(self, transaction_data): self.send_lock.acquire() if self.send_transaction_cache.contains(transaction_data): self.send_lock.release() return if len(transaction_data) > self.MAX_RELAY_TRANSACTION_BYTES and (len(transaction_data) > self.MAX_RELAY_OVERSIZE_TRANSACTION_BYTES or self.send_transaction_cache.get_flag_count() >= MAX_EXTRA_OVERSIZE_TRANSACTIONS): self.send_lock.release() return try: relay_data = pack('>3I', self.MAGIC_BYTES, self.TRANSACTION_TYPE, len(transaction_data)) relay_data += transaction_data self.relay_sock.sendall(relay_data) self.send_transaction_cache.add(transaction_data, len(transaction_data) > self.MAX_RELAY_OVERSIZE_TRANSACTION_BYTES) if deserialize_utils: transaction = CTransaction.deserialize(transaction_data) print("Sent transaction " + str(b2lx(transaction.GetHash())) + " of size " + str(len(transaction_data))) else: print("Sent transaction of size " + str(len(transaction_data))) except (OSError, socket.error) as err: print("Failed to send to relay node: ", err) self.send_lock.release()
def spend_escrow(serial_tx, redeem_script, payer_sig, redeemer_sig): ''' Sends a transaction fulfilling the redeem script of escrow tx ''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Set script sig txin.scriptSig = CScript([OP_0, payer_sig + '\x01', redeemer_sig + '\x01', redeem_script]) # Verify script redeem_script = CScript(redeem_script) serial_tx = tx.serialize() VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) serial_tx = tx.serialize() txid = b2lx(Hash(serial_tx)) print("spend_escrow: TXID is %s" % txid) print("spend_escrow: RAW TX is %s" % b2x(serial_tx)) return serial_tx
def adjust_transaction(tx_hex): # if you don't want change you wallet signature code # you can use this to adjust high S signature to a canonical signature tx = CTransaction.deserialize(x(tx_hex)) for vin in tx.vin: script_lst = list(vin.scriptSig) sig = script_lst[0] # normal signature format <sig> <pubkey> if len(script_lst) == 2 and not IsLowDERSignature(sig): low_s_sig = signature_to_low_s(sig)#.encode('hex') #print sig.encode('hex'), low_s_sig.encode('hex') script_lst[0] = low_s_sig vin.scriptSig = CScript(script_lst) # 2/2 or 2/3 multisig format <0> <signature> <signature> <redeemscript> elif len(script_lst) == 4: script_lst_new = [] for idx, sig in enumerate(script_lst[1:3]): if not IsLowDERSignature(sig): low_s_sig = signature_to_low_s(sig)#.encode('hex') #print sig.encode('hex'), low_s_sig.encode('hex') script_lst_new.append(low_s_sig) else: script_lst_new.append(sig) script_lst_new.append(script_lst[3]) vin.scriptSig = "\x00" + CScript(script_lst_new) return b2x(tx.serialize())
def assert_tx_matches(serialized_tx: bytes, hash_link: str, tx_hex: str = None) -> None: """Verifies if a transaction is correctly formed.""" tx_id = hash_link.split("/")[-1] parsed_tx = CTransaction.deserialize(serialized_tx) assert tx_id == parsed_tx.GetTxid()[::-1].hex() if tx_hex: assert serialized_tx.hex() == tx_hex # TODO: we could probably do better than os.environ, this was the easiest solution # (we could create a pytest option (and use config.getoption("check-on-chain")), # but then each test would need to have access to config via function argument) if int(os.environ.get("CHECK_ON_CHAIN", 0)): def get_tx_hex(hash_link: str) -> str: tx_data = requests.get(hash_link, headers={ "User-Agent": "BTC transactions test" }).json(parse_float=Decimal) return tx_data["hex"] assert serialized_tx.hex() == get_tx_hex(hash_link)
def add_input(self, serial_id: int, prevtx: str, prevtx_vout: int, max_witness_len: int, script: str, sequence: int, privkey: str = None) -> None: # Find the txid of the transaction prev_tx = CTransaction.deserialize(bytes.fromhex(prevtx)) txin = CTxIn(COutPoint(prev_tx.GetTxid(), prevtx_vout), nSequence=sequence) # Get the previous output for its outscript + value prev_vout = prev_tx.vout[prevtx_vout] self.inputs.append({ 'input': txin, 'serial_id': serial_id, 'sats': prev_vout.nValue, 'prev_outscript': prev_vout.scriptPubKey.hex(), 'redeemscript': script, 'max_witness_len': max_witness_len, 'privkey': privkey, })
def cmd_mkwitness(args): tx = None if args.tx is not None: serialized_tx = args.tx tx = CTransaction.deserialize(serialized_tx) else: tx = args.proxy.getrawtransaction(args.txid) txproof = TxProof(tx=tx) for seal_fd in args.seal_fds: seal = BitcoinSingleUseSeal.deserialize(seal_fd.read()) txinproof = None txoutproof = None for i, txin in enumerate(txproof.tx.vin): if txin.prevout == seal.outpoint: txinproof = TxInProof(i=i, txproof=txproof) txoutproof = TxOutProof(i=0, txproof=txproof) break else: args.parser.error("Seal '%s' not closed by this transaction" % seal_fd.name) witness = BitcoinSealWitness(seal=seal, txinproof=txinproof, txoutproof=txoutproof) witness_filename = seal_fd.name + '.witness' logging.info("Creating witness file '%s'" % witness_filename) with open(seal_fd.name + '.witness', 'xb') as witness_fd: witness_fd.write(witness.serialize())
def spend_escrow(self, payer_sig, redeemer_sig, serial_tx, redeem_script): ''' Sends a transaction fulfilling the redeem script of escrow tx ''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Set script sig txin.scriptSig = CScript([ OP_FALSE, payer_sig + '\x01', redeemer_sig + '\x01', OP_TRUE, redeem_script ]) # Verify script redeem_script = CScript(redeem_script) serial_tx = tx.serialize() VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) serial_tx = tx.serialize() if not self.test: # txid = self.proxy.sendrawtransaction(tx) txid = b2lx(Hash(serial_tx)) else: txid = b2lx(Hash(serial_tx)) self.logger.info("spend_escrow: TXID is %s", txid) self.logger.info("spend_escrow: RAW TX is %s", b2x(serial_tx)) return serial_tx
def createrawtransaction(self, *args): """Get rawtransactions when provided vin and vout FIXME: Implement options and accept outpoints instead of user args """ r = self._call('createrawtransaction', *args) r = str(r) tx = CTransaction.deserialize(unhexlify(r)) return tx
def analyze_tx(tx_hex_string): output = {} # get op_return from transaction hex = unhexlify(tx_hex_string) deserializedTransaction = CTransaction.deserialize(hex) op_return_vout = deserializedTransaction.vout[1].scriptPubKey # get redeem script redeem_script = '' for i in op_return_vout: script = bytes(i).decode('utf8') if 'REDEEM' in script: redeem_script_string = script.replace('REDEEM SCRIPT ', '') output['redeemScript'] = redeem_script_string # convert redeem script into list redeemScript = CScript(unhexlify(redeem_script_string)) redeem_script_array = [] for i in redeemScript: redeem_script_array.append(i) # get redeem script hash (hodl address) p2sh_address = P2SHBitcoinAddress.from_redeemScript(redeemScript) output['hodlAddress'] = str(p2sh_address) # get nlocktime from redeem script nlocktime_hex = b2lx(redeem_script_array[0]) nlocktime = int(nlocktime_hex, 16) output['nLockTime'] = nlocktime # get authorized key from redeem script pubkey = b2x(redeem_script_array[3]) # get address from authorized key pubkey = unhexlify(pubkey) P2PKHBitcoinAddress = bitcoin.wallet.P2PKHBitcoinAddress addr = P2PKHBitcoinAddress.from_pubkey(pubkey) output['authorizedAddress'] = str(addr) # get total sent to hodl address locked_satoshis = 0 for i in deserializedTransaction.vout: if i.nValue > 0: sPK = i.scriptPubKey amount = i.nValue try: vout_p2sh_addr = P2SHBitcoinAddress.from_scriptPubKey(sPK) # rewards only paid to really locked funds if str(p2sh_address) == str(vout_p2sh_addr): locked_satoshis += amount except: pass output["lockedSatoshis"] = locked_satoshis return (output)
def signrawtransaction(self, tx, *args): """Sign inputs for transaction FIXME: implement options """ hextx = hexlify(tx.serialize()) r = self._call('signrawtransaction', hextx, *args) r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) del r['hex'] return r
def sign_refund_tx(self, tx_hex, key_no=1, actor="us"): key_no -= 1 if key_no == 0: ecdsa = self.ecdsa_us[0] if key_no == 1: ecdsa = self.ecdsa_us[1] tx = CTransaction.deserialize(binascii.unhexlify(tx_hex)) sighash = SignatureHash(bond_redeem_script(self.ecdsa_us, self.ecdsa_them, self.factory.ecdsa_arbiters[0], actor)["bin"], tx, 0, SIGHASH_ALL) seckey = CBitcoinSecret.from_secret_bytes(ecdsa.get_private_key("bin"), compressed=True) sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) return sig
def signrawtransactionwithwallet(self, tx, *args): """Sign inputs for transaction bicoincore >= 0.17.x FIXME: implement options """ hextx = hexlify(tx.serialize()) r = self._call('signrawtransactionwithwallet', hextx, *args) r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) del r['hex'] return r
def mutate_tx(tx_hex): """ Mutates a raw transaction using TX malleability in the scriptSig (specifically, the OP codes.) This function shouldn't be used beyond testing as it uses an ugly eval() hack. https://en.bitcoin.it/wiki/Transaction_Malleability """ tx = CTransaction.deserialize(binascii.unhexlify(tx_hex)) script_sig = repr(tx.vin[0].scriptSig)[9:] script_sig = eval("CScript([OP_1, OP_DROP, " + script_sig) tx.vin[0].scriptSig = script_sig return b2x(tx.serialize())
def set_tx(self): """Set the spending transaction and (en|dis)able the input index box.""" txt = str(self.tx_edit.toPlainText()) if not txt: self.tx = None self.input_idx.setEnabled(False) self.tx_edit.setToolTip('') return self.tx = CTransaction.deserialize(txt.decode('hex')) self.tx_edit.setToolTip(''.join(['Tx ID: ', bitcoin.core.b2lx(self.tx.GetHash())])) self.input_idx.setRange(0, len(self.tx.vin) - 1) self.input_idx.setEnabled(True)
def __init__(self, tx_hex, targetScriptPubkey: CScript): self.tx = CTransaction.deserialize(x(tx_hex)) self.txid = b2lx(self.tx.GetTxid()) # TODO: assuming there is only one output with targetScriptPubkey self.nout = None for i, out in enumerate(self.tx.vout): if out.scriptPubKey == targetScriptPubkey: self.nout = i break assert self.nout is not None self.outpoint = COutPoint(lx(self.txid), self.nout)
def set_transaction(self, name, tx_hex): self.transactions[name]["hex"] = tx_hex if tx_hex == None: self.transactions[name]["CTransaction"] = None self.transactions[name]["txid_hex"] = None else: self.transactions[name]["CTransaction"] = CTransaction.deserialize( binascii.unhexlify(self.transactions[name]["hex"])) self.transactions[name]["txid_hex"] = self.calculate_txid( self.transactions[name]["hex"]) self.transactions[name]["is_broadcast"] = 0 self.transactions[name]["confirmations"] = 0 self.transactions[name]["malleability_occured"] = 0
def signrawtransaction(self, tx, *args): """Sign inputs for transaction FIXME: implement options """ if getattr(tx, "serialize", None) is not None: hextx = hexlify(tx.serialize()) else: # then assume we got a raw string already hextx = tx r = self._call('signrawtransaction', hextx, *args) r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) del r['hex'] return r
def action(self, runner: 'Runner') -> bool: super().action(runner) utxo_tx = self.resolve_arg('utxo_tx', runner, self.utxo_tx) txid = CTransaction.deserialize( bytes.fromhex(utxo_tx)).GetTxid()[::-1].hex() runner.init_rbf( self, self.find_conn(runner), self.resolve_arg('channel_id', runner, self.channel_id), self.resolve_arg('amount', runner, self.amount), txid, self.resolve_arg('utxo_outnum', runner, self.utxo_outnum), self.feerate) return True
def sign_setup_tx(tx_hex, redeem_script, ecdsa): tx = CTransaction.deserialize(binascii.unhexlify(tx_hex)) sighash = SignatureHash(redeem_script["bin"], tx, 0, SIGHASH_ALL) print(b"Signing = " + sighash) print(ecdsa.get_public_key()) seckey = CBitcoinSecret.from_secret_bytes(ecdsa.get_private_key("bin"), compressed=True) sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) print(b"Pub key = " + ecdsa.get_public_key("bin")) print(b"Sig = " + sig) print() return sig
def get_utxo(self, address): """Gets all the Unspent Transaction Outs from a given <address> """ script_pubkey = CBitcoinAddress(address).to_scriptPubKey() txs = self.get_response("blockchain.address.get_history", [address]) spent = {} utxos = [] for tx in txs: raw = self.get_raw_transaction(tx["tx_hash"], tx["height"]) data = CTransaction.deserialize(to_binary(raw)) for vin in data.vin: spent[(to_little_endian_hex(vin.prevout.hash), vin.prevout.n)] = 1 for outindex, vout in enumerate(data.vout): if vout.scriptPubKey == script_pubkey: utxos += [(tx["tx_hash"], outindex, vout.nValue, to_hex(vout.scriptPubKey))] return [u for u in utxos if not u[0:2] in spent]
def get_utxo(self, address): script_pubkey = CBitcoinAddress(address).to_scriptPubKey() txs = self.get_response('blockchain.address.get_history', [address]) spent = {} utxos = [] for tx in txs: print tx raw = self.get_raw_transaction(tx['tx_hash'], tx['height']) data = CTransaction.deserialize(to_binary(raw)) for vin in data.vin: spent[(to_little_endian_hex(vin.prevout.hash), vin.prevout.n)] = 1 for outindex, vout in enumerate(data.vout): if vout.scriptPubKey == script_pubkey: utxos += [(tx['tx_hash'], outindex, vout.nValue, to_hex(vout.scriptPubKey))] return [u for u in utxos if not u[0:2] in spent]
def spend_preimage(redeem_script, preimages, redeemer_sig, serial_tx): """ Creates a transaction fulfilling the redeem script of the preimage P2SH. Arguements: redeem_script (bytes): The script that specifies the conditions that a tx has to fulfill to transfer funds from the `funding_tx` preimages (list): The preimages that hash into the hash values specified in the `redeem_script` redeemer_sig (bytes): The signature of the redeemer on the `serial_tx` serial_tx (bytes): The serial transaction Returns: The serial raw transaction that passes the script verification """ # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Setup preimages in reverse order script = [] for p in reversed(preimages): script += [p] # Create script sig txin.scriptSig = CScript([redeemer_sig + '\x01'] + script + [redeem_script]) # Verify script redeem_script = CScript(redeem_script) try: VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) except ValidationError: print("spend_preimage: Script failed to verify") return None serial_tx = tx.serialize() txid = b2lx(Hash(serial_tx)) print("spend_preimage: TXID is %s" % txid) print("spend_preimage: RAW TX is %s" % b2x(serial_tx)) return serial_tx
def deserialize(self): self.clear() txt = str(self.raw_tx_edit.toPlainText()) try: txt = txt.decode('hex') except Exception: self.status_message('Raw transaction must be hex.', True) return try: self.tx = tx = CTransaction.deserialize(txt) except Exception: self.status_message('Cannot deserialize transaction.', True) return self.tx_widget.set_tx(tx) self.status_message('Deserialized transaction {}'.format(bitcoin.core.b2lx(tx.GetHash())))
def test_path_from_msg_to_txid(self): def T(msg, tx): path = path_from_msg_to_txid(msg, tx) self.assertEqual(path(msg), tx.GetHash()) tx = CTransaction.deserialize(x('0100000001e853c9e0c133547fd9e162b1d3860dd0f27d5b9b8a7430d28896c00fbb3f1bc7000000008c49304602210095bcd54ebd0caa7cee75f0f89de472a765e6ef4b98c5fd4b32c7f9d4905db9ae022100ebd3f668e3a1a36d56e30184c27531dbb9fc136c84b1282be562064d86997d1e014104727eb4fdcc90658cd26abe7dcb0ae7297810b15b9e27c32bcf8e3edd934901968806dc18b1276d7273cc4c223feee0070361ed947888a3cef422bebfede96e08ffffffff020065cd1d000000001976a91468c6c2b3c0bc4a8eeb10d16a300d627a31a3b58588ac0008af2f000000001976a9141d87f0a54a1d704ffc70eae83b025698bc0fdcfc88ac00000000')) # txid in vin T(lx('c71b3fbb0fc09688d230748a9b5b7df2d00d86d3b162e1d97f5433c1e0c953e8'), tx) # part of a script T(x('1d87f0a54a1d704ffc70eae83b025698bc0fdcfc'), tx) # beginning of the tx T(x('0100000001e853c9e0c133547fd9'), tx) # end of the tx T(x('98bc0fdcfc88ac00000000'), tx)
def spend_escrow(redeem_script, payer_sig, redeemer_sig, serial_tx): """ Creates a transaction fulfilling the redeem script of the escrow P2SH. Arguements: redeem_script (bytes): The script that specifies the conditions that a tx has to fulfill to transfer funds from the `funding_tx` payer_sig (bytes): The signature of the payer on the `serial_tx` redeemer_sig (bytes): The signature of the redeemer on the `serial_tx` serial_tx (bytes): The serial transaction Returns: The serial raw transaction that passes the script verification """ # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Set script sig txin.scriptSig = CScript( [OP_0, payer_sig + '\x01', redeemer_sig + '\x01', redeem_script]) # Verify script redeem_script = CScript(redeem_script) serial_tx = tx.serialize() try: VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) except ValidationError: print("spend_escrow: Script failed to verify") return None serial_tx = tx.serialize() txid = b2lx(Hash(serial_tx)) print("spend_escrow: TXID is %s" % txid) print("spend_escrow: RAW TX is %s" % b2x(serial_tx)) return serial_tx
def load_test_vectors(name): with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd: for test_case in json.load(fd): # Comments designated by single length strings if len(test_case) == 1: continue assert len(test_case) == 3 prevouts = {} for json_prevout in test_case[0]: assert len(json_prevout) == 3 n = json_prevout[1] if n == -1: n = 0xffffffff prevout = COutPoint(lx(json_prevout[0]), n) prevouts[prevout] = parse_script(json_prevout[2]) tx = CTransaction.deserialize(x(test_case[1])) enforceP2SH = test_case[2] yield (prevouts, tx, enforceP2SH)
def fundrawtransaction(self, tx, include_watching=False): """Add inputs to a transaction until it has enough in value to meet its out value. include_watching - Also select inputs which are watch only Returns dict: {'tx': Resulting tx, 'fee': Fee the resulting transaction pays, 'changepos': Position of added change output, or -1, } """ hextx = hexlify(tx.serialize()) r = self._call('fundrawtransaction', hextx, include_watching) r['tx'] = CTransaction.deserialize(unhexlify(r['hex'])) del r['hex'] r['fee'] = int(r['fee'] * COIN) return r
def get_keys_from_tx(self, serial_tx, n_keys=15): '''Extracts n_keys from tx in serial form''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) # Keys are in txin.scriptSig txin = tx.vin[0] script = txin.scriptSig # Extract keys from script keys = [] for i, op in enumerate(script): if i in range(1, n_keys + 1): keys += [op] # Serialize keys in correct order serial_keys = "" for op in reversed(keys): serial_keys += op return serial_keys
def check_setup_works(tx_hex, redeem_script, owner_first_sig, owner_second_sig, third_party_sig): try: tx = CTransaction.deserialize(binascii.unhexlify(tx_hex)) redeem_script_hash160 = hash160_script(redeem_script) if owner_first_sig != None and owner_second_sig != None and third_party_sig != None: tx.vin[0].scriptSig = CScript([OP_0, owner_first_sig, owner_second_sig, third_party_sig, redeem_script["bin"]]) p2sh_script_pub_key = CScript([OP_HASH160, redeem_script_hash160["bin"], OP_EQUAL]) VerifyScript(tx.vin[0].scriptSig, p2sh_script_pub_key, tx, 0, (SCRIPT_VERIFY_P2SH,)) signed_tx_hex = b2x(tx.serialize()) return { "tx_hex": signed_tx_hex, "txid": calculate_txid(signed_tx_hex) } except Exception as e: error = parse_exception(e) log_exception(error_log_path, error) print(error) print("Check setup works failed.") print(e) return None
def deserialize_raw_transaction(raw_transaction: str) -> CTransaction: ''' Checking if transaction can be deserialized (is not corrupted). Args: raw_transaction (str): transaction to check Returns: CTransaction: transaction object Raises: ImpossibleDeserialization: when transaction is corrupted Example: >>> from clove.network import Litecoin >>> network = Litecoin() >>> network.deserialize_raw_transaction('0100000001aa25fd5f63cb41d6ee7dd495256046b4c3f17d4540a1b258a06bfefac30da60900000000fdff0047304402201c8869d359b5599ecffd51a96f0a8799392c98c4e15242762ba455e37b1f5d6302203f2974e9afc8d641f9363167df48e5a845a8deba1381bf5a1b549ac04718a1ac01410459cdb91eb7298bc2578dc4e7ac2109ac3cfd9dc9818795c5583e720d2114d540724bf26b4541f683ff51968db627a04eecd1f5cff615b6350dad5fb595f8adf420c480afb333623864901c968022a07dd93fe3c06f5684ea728b8113e17fa91bd9514c5163a61450314a793bf317665ecdc54c2e843bb106aeee158876a91485c0522f6e23beb11cc3d066cd20ed732648a4e66704926db75bb17576a914621f617c765c3caa5ce1bb67f6a3e51382b8da296888ac00000000015a7b0100000000001976a91485c0522f6e23beb11cc3d066cd20ed732648a4e688ac00000000') # noqa: E501 CTransaction((CTxIn(COutPoint(lx('09a60dc3fafe6ba058b2a140457df1c3b446602595d47deed641cb635ffd25aa'), 0), CScript([x('304402201c8869d359b5599ecffd51a96f0a8799392c98c4e15242762ba455e37b1f5d6302203f2974e9afc8d641f9363167df48e5a845a8deba1381bf5a1b549ac04718a1ac01'), x('0459cdb91eb7298bc2578dc4e7ac2109ac3cfd9dc9818795c5583e720d2114d540724bf26b4541f683ff51968db627a04eecd1f5cff615b6350dad5fb595f8adf4'), x('c480afb333623864901c968022a07dd93fe3c06f5684ea728b8113e17fa91bd9'), 1, x('63a61450314a793bf317665ecdc54c2e843bb106aeee158876a91485c0522f6e23beb11cc3d066cd20ed732648a4e66704926db75bb17576a914621f617c765c3caa5ce1bb67f6a3e51382b8da296888ac')]), 0x0),), (CTxOut(0.00097114*COIN, CScript([OP_DUP, OP_HASH160, x('85c0522f6e23beb11cc3d066cd20ed732648a4e6'), OP_EQUALVERIFY, OP_CHECKSIG])),), 0, 1, CTxWitness()) # noqa: E501 ''' try: return CTransaction.deserialize(x(raw_transaction)) except Exception: raise ImpossibleDeserialization()
def spend_preimage(self, preimages, redeemer_sig, serial_tx, redeem_script): ''' Sends a transaction fulfilling the redeem script of the preimage P2SH ''' # Read in transaction temp_tx = CTransaction.deserialize(serial_tx) tx = CMutableTransaction.from_tx(temp_tx) txin = tx.vin[0] # Setup preimages in reverse order script = [] for p in reversed(preimages): script += [p] # Create script sig txin.scriptSig = CScript([redeemer_sig + '\x01'] + script + [OP_TRUE, redeem_script]) # Verify script redeem_script = CScript(redeem_script) VerifyScript(txin.scriptSig, redeem_script.to_p2sh_scriptPubKey(), tx, 0, [SCRIPT_VERIFY_P2SH]) serial_tx = tx.serialize() if not self.test: # txid = self.proxy.sendrawtransaction(tx) txid = b2lx(Hash(serial_tx)) else: txid = b2lx(Hash(serial_tx)) self.logger.info("spend_preimage: TXID is %s", txid) self.logger.info("spend_preimage: RAW TX is %s", b2x(serial_tx)) return serial_tx
def broadcast_tx(self, transaction): as_hex = transaction.as_hex() transaction = CTransaction.deserialize(unhexlify(as_hex)) tx_id = bitcoin.rpc.Proxy().sendrawtransaction(transaction) # reverse endianness for bitcoind return hexlify(bytearray(tx_id)[::-1])
def get_tx(self, txhash): """Get the transaction object given a transaction hash. """ txhex = self.get_raw_transaction(txhash) tx = CTransaction.deserialize(to_binary(txhex)) return blockchain.CTransaction.from_bitcoincore(txhash, tx, self)
def str_tree(self, indent=0, verbosity=0): """Convert to tree (for debugging)""" class bcolors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' def str_result(verb, parameter, result): rr = "" if verb > 0 and result is not None: rr += " == " result_hex = b2x(result) if parameter is not None: parameter_hex = b2x(parameter) try: index = result_hex.index(parameter_hex) parameter_hex_highlight = bcolors.BOLD + parameter_hex + bcolors.ENDC if index == 0: rr += parameter_hex_highlight + result_hex[index+len(parameter_hex):] else: rr += result_hex[0:index] + parameter_hex_highlight except ValueError: rr += result_hex else: rr += result_hex return rr r = "" if len(self.attestations) > 0: for attestation in sorted(self.attestations): r += " "*indent + "verify %s" % str(attestation) + str_result(verbosity, self.msg, None) + "\n" if attestation.__class__ == BitcoinBlockHeaderAttestation: r += " "*indent + "# Bitcoin block merkle root " + b2lx(self.msg) + "\n" if len(self.ops) > 1: for op, timestamp in sorted(self.ops.items()): try: CTransaction.deserialize(self.msg) r += " " * indent + "* Bitcoin transaction id " + b2lx( OpSHA256()(OpSHA256()(self.msg))) + "\n" except SerializationError: pass cur_res = op(self.msg) cur_par = op[0] r += " " * indent + " -> " + "%s" % str(op) + str_result(verbosity, cur_par, cur_res) + "\n" r += timestamp.str_tree(indent+4, verbosity=verbosity) elif len(self.ops) > 0: try: CTransaction.deserialize(self.msg) r += " " * indent + "# Bitcoin transaction id " + \ b2lx(OpSHA256()(OpSHA256()(self.msg))) + "\n" except SerializationError: pass op = tuple(self.ops.keys())[0] cur_res = op(self.msg) cur_par = op[0] if len(op) > 0 else None r += " " * indent + "%s" % str(op) + str_result(verbosity, cur_par, cur_res) + "\n" r += tuple(self.ops.values())[0].str_tree(indent, verbosity=verbosity) return r
def broadcast_tx(self, transaction): as_hex = transaction.as_hex() transaction = CTransaction.deserialize(h2b(as_hex)) tx_id = bitcoin.rpc.Proxy().sendrawtransaction(transaction) # reverse endianness for bitcoind return b2h_rev(tx_id)
def bitcoind_broadcast(hextx): tx = CTransaction.deserialize(unhexlify(hextx)) txid = bitcoin.rpc.Proxy().sendrawtransaction(tx) return hexlify(txid)
def compare_transactions(tx_hex1, tx_hex2): """ This function compares two transactions. It is required because attackers can change serialized transactions without invalidating the transaction by modifying a scriptSig or by changing a signature which would change the transaction ID. This function considers transactions equivalent if they share the same inputs and outputs. The scriptSig doesn't matter for the purposes of this function. The risk of replacing a valid transaction with an invalid one with false scriptSigs doesn't occur as subsequent code [in this module] only replaces transactions if they have at least 1 confirmation. I'm sure Bitcoind also wouldn't allow invalid transactions into the mempool, disk, etc. """ try: if tx_hex1 == tx_hex2: #Well, that was easy. Now for the hard part. return 1 tx1 = CTransaction.deserialize(binascii.unhexlify(tx_hex1)) tx2 = CTransaction.deserialize(binascii.unhexlify(tx_hex2)) #Compare number of inputs. if len(tx1.vin) != len(tx2.vin): return 0 #Compare number of outputs. if len(tx1.vout) != len(tx2.vout): return 0 #Compare nVersion. if tx1.nVersion != tx2.nVersion: return 0 #Compare nLockTime. if tx1.nLockTime != tx2.nLockTime: return 0 #Compare inputs. for i in range(0, len(tx1.vin)): #Compare sequences. #To keep it simple: tx replacement isn't supported. if tx1.vin[i].nSequence != tx2.vin[i].nSequence: return 0 #Compare outpoints. if tx1.vin[i].prevout.hash != tx2.vin[i].prevout.hash: return 0 if tx1.vin[i].nSequence != tx2.vin[i].nSequence: return 0 #Compare outputs. for i in range(0, len(tx2.vout)): if tx1.vout[i] != tx2.vout[i]: return 0 #Sighash and sigs aren't checked as this code is run on TXs returned from *coind. return 1 except: return 0
def adjust_refund_tx(self, our_setup_txid, their_setup_tx_hex, their_refund_tx_hex, received_tx_hex=None, their_first_sig=None, their_second_sig=None): #Calculate chunk sizes. remaining = self.upload_amount - self.trade.sent send_chunk_size = self.our_chunk_size if send_chunk_size > remaining: send_chunk_size = remaining remaining = self.download_amount - self.trade.recv recv_chunk_size = self.their_chunk_size if recv_chunk_size > remaining: recv_chunk_size = remaining #Validate transactions. if received_tx_hex != None and their_first_sig != None and their_second_sig != None: #Check their refund spends the output of the bond. their_refund_tx = CTransaction.deserialize(binascii.unhexlify(their_refund_tx_hex)) their_setup_txid = calculate_txid(their_setup_tx_hex) if reverse_hex(binascii.hexlify(their_refund_tx.vin[0].prevout.hash).decode("utf-8")) != their_setup_txid: print("11111") return None else: """ try: #Ensure the bond transaction has been broadcast. self.recv_coin_rpc.sendrawtransaction(their_bond_tx_hex) #(Subsequent code will fail since bond has only just been broadcast.) #return None except Exception as e: #Transaction already in block chain. pass """ #Check our received payment is as expected. unsigned_tx = CTransaction.deserialize(binascii.unhexlify(received_tx_hex)) expected = self.trade.recv + recv_chunk_size #Check transaction input. their_alleged_setup_txid = reverse_hex(binascii.hexlify(unsigned_tx.vin[0].prevout.hash).decode("utf-8")) if their_alleged_setup_txid != their_setup_txid: #Give them the benefit of the doubt - look for tx malluability. alleged_setup_tx_hex = recv_coin_rpc.getrawtransaction(their_alleged_setup_txid) if not compare_transactions(alleged_setup_tx_hex, their_setup_tx_hex): print("22222222@") return None #This is what our output -should- look like. our_address = deconstruct_address(self.our_address)["hash"] our_pub_key = CScript([OP_DUP, OP_HASH160, our_address, OP_EQUALVERIFY, OP_CHECKSIG]) #Check an output goes to us with expected amount. amount_found = 0 for output in unsigned_tx.vout: print(output.scriptPubKey) print(our_pub_key) if output.scriptPubKey == our_pub_key: amount_found = Decimal(str_money_value(output.nValue)) break if not amount_found: print(our_pub_key) print(unsigned_tx.vout) print("333333333333") return None else: #Check amount. if amount_found < expected.as_decimal: print("43535345346") print(amount_found) print(expected.as_decimal) return None #Check transaction isn't time locked. if unsigned_tx.nLockTime: print("4444444444") return None #Check sequences are final. for input in unsigned_tx.vin: if input.nSequence != 0xffffffff: print("55555555555") return None #Check transaction can be spent. our_first_sig = self.sign_refund_tx(received_tx_hex, 1, "them") ret = self.check_refund_works(received_tx_hex, their_first_sig, their_second_sig, our_first_sig, "them") if ret == None: print("6666666666666") return None self.details["our_download"] = ret else: #The first call to this function has nothing to evaluate -- you are receiving nothing. recv_chunk_size = 0 #Adjust refund. refund_amount = self.trade.sent + send_chunk_size print("sdfsdfsdf--------") print(refund_amount) refund_tx_hex = self.build_refund_tx(our_setup_txid, refund_amount)["tx_hex"] self.trade.sent += send_chunk_size self.trade.recv += recv_chunk_size #Save details. self.update() #Is transfer complete? I.e. no change = transfer complete. if send_chunk_size == C(0) and recv_chunk_size == C(0): #I liek chocolate milk. return 1 #Return result. our_first_sig = self.sign_refund_tx(refund_tx_hex, 1) our_second_sig = self.sign_refund_tx(refund_tx_hex, 2) return { "tx_hex": refund_tx_hex, "first_sig": our_first_sig, "second_sig": our_second_sig }
def ctx_deserialize(cls, ctx): serialized_tx = ctx.read_bytes('tx') return CTransaction.deserialize(serialized_tx)
def str_tree(self, indent=0, verbosity=0): """Convert to tree (for debugging)""" class bcolors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' def str_result(verb, parameter, result): rr = "" if verb > 0 and result is not None: rr += " == " result_hex = b2x(result) if parameter is not None: parameter_hex = b2x(parameter) try: index = result_hex.index(parameter_hex) parameter_hex_highlight = bcolors.BOLD + parameter_hex + bcolors.ENDC if index == 0: rr += parameter_hex_highlight + result_hex[ index + len(parameter_hex):] else: rr += result_hex[0:index] + parameter_hex_highlight except ValueError: rr += result_hex else: rr += result_hex return rr r = "" if len(self.attestations) > 0: for attestation in sorted(self.attestations): r += " " * indent + "verify %s" % str( attestation) + str_result(verbosity, self.msg, None) + "\n" if attestation.__class__ == BitcoinBlockHeaderAttestation: r += " " * indent + "# Bitcoin block merkle root " + b2lx( self.msg) + "\n" if len(self.ops) > 1: for op, timestamp in sorted(self.ops.items()): try: CTransaction.deserialize(self.msg) r += " " * indent + "* Bitcoin transaction id " + b2lx( OpSHA256()(OpSHA256()(self.msg))) + "\n" except SerializationError: pass cur_res = op(self.msg) cur_par = op[0] r += " " * indent + " -> " + "%s" % str(op) + str_result( verbosity, cur_par, cur_res) + "\n" r += timestamp.str_tree(indent + 4, verbosity=verbosity) elif len(self.ops) > 0: try: CTransaction.deserialize(self.msg) r += " " * indent + "# Bitcoin transaction id " + \ b2lx(OpSHA256()(OpSHA256()(self.msg))) + "\n" except SerializationError: pass op = tuple(self.ops.keys())[0] cur_res = op(self.msg) cur_par = op[0] if len(op) > 0 else None r += " " * indent + "%s" % str(op) + str_result( verbosity, cur_par, cur_res) + "\n" r += tuple(self.ops.values())[0].str_tree(indent, verbosity=verbosity) return r
if args.testnet: bitcoin.SelectParams('testnet') proxy = bitcoin.rpc.Proxy() txins = [] prevouts = set() sum_value_in = 0 line = -1 for l in sys.stdin.readlines(): line += 1 l = l.strip() try: tx = CTransaction.deserialize(x(l)) except Exception: continue for txin in tx.vin: try: txout_info = proxy.gettxout(txin.prevout) except IndexError: print('Already spent! line %d, txid %s %d' % \ (line, b2lx(txin.prevout.hash), txin.prevout.n), file=sys.stderr) continue print('line %d: %s %d: %s' % \ (line, b2lx(txin.prevout.hash), txin.prevout.n,
def __init__(self, txid, rawhex): self.ct = CTransaction.deserialize(x(rawhex)) self.txid = txid
def deserialize_raw_transaction(raw_transaction: str) -> CTransaction: try: return CTransaction.deserialize(x(raw_transaction)) except Exception: raise ImpossibleDeserialization()
def connect(self): self.send_lock.acquire() self.recv_transaction_cache = FlaggedArraySet(1000) self.send_transaction_cache = FlaggedArraySet(1000) self.relay_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.relay_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) try: try: self.relay_sock.connect((self.server, 8336)) self.relay_sock.sendall(pack('>3I', self.MAGIC_BYTES, self.VERSION_TYPE, len(self.VERSION_STRING))) self.relay_sock.sendall(self.VERSION_STRING) finally: self.send_lock.release() while True: msg_header = unpack('>3I', self.relay_sock.recv(3 * 4, socket.MSG_WAITALL)) if msg_header[0] != self.MAGIC_BYTES: raise ProtocolError("Invalid magic bytes: " + str(msg_header[0]) + " != " + str(self.MAGIC_BYTES)) if msg_header[2] > 1000000: raise ProtocolError("Got message too large: " + str(msg_header[2])) if msg_header[1] == self.VERSION_TYPE: version = self.relay_sock.recv(msg_header[2], socket.MSG_WAITALL) if version != self.VERSION_STRING: raise ProtocolError("Got back unknown version type " + str(version)) print("Connected to relay node with protocol version " + str(version)) elif msg_header[1] == self.BLOCK_TYPE: if msg_header[2] > 10000: raise ProtocolError("Got a BLOCK message with far too many transactions: " + str(msg_header[2])) wire_bytes = 3 * 4 header_data = self.relay_sock.recv(80, socket.MSG_WAITALL) wire_bytes += 80 self.data_recipient.provide_block_header(header_data) if deserialize_utils: header = CBlockHeader.deserialize(header_data) print("Got block header: " + str(b2lx(header.GetHash()))) if msg_header[2] < 0xfd: block_data = header_data + pack('B', msg_header[2]) elif msg_header[2] < 0xffff: block_data = header_data + b'\xfd' + pack('<H', msg_header[2]) elif msg_header[2] < 0xffffffff: block_data = header_data + b'\xfe' + pack('<I', msg_header[2]) else: raise ProtocolError("WTF?????") for i in range(0, msg_header[2]): index = unpack('>H', self.relay_sock.recv(2, socket.MSG_WAITALL))[0] wire_bytes += 2 if index == 0xffff: data_length = unpack('>HB', self.relay_sock.recv(3, socket.MSG_WAITALL)) wire_bytes += 3 data_length = data_length[0] << 8 | data_length[1] if data_length > 1000000: raise ProtocolError("Got in-block transaction of size > MAX_BLOCK_SIZE: " + str(dat_length)) transaction_data = self.relay_sock.recv(data_length, socket.MSG_WAITALL) wire_bytes += data_length if deserialize_utils: transaction = CTransaction.deserialize(transaction_data) print("Got in-block full transaction: " + str(b2lx(transaction.GetHash())) + " of length " + str(data_length)) else: print("Got in-block full transaction of length " + str(data_length)) block_data += transaction_data else: transaction_data = self.recv_transaction_cache.get_by_index(index) if transaction_data is None: raise ProtocolError("Got index for a transaction we didn't have") self.recv_transaction_cache.remove(transaction_data) block_data += transaction_data self.data_recipient.provide_block(block_data) if deserialize_utils: print("Got full block " + str(b2lx(header.GetHash())) + " with " + str(msg_header[2]) + " transactions in " + str(wire_bytes) + " wire bytes") block = CBlock.deserialize(block_data) print("Deserialized full block " + str(b2lx(block.GetHash()))) else: print("Got full block with " + str(msg_header[2]) + " transactions in " + str(wire_bytes) + " wire bytes") if unpack('>3I', self.relay_sock.recv(3 * 4, socket.MSG_WAITALL)) != (self.MAGIC_BYTES, self.END_BLOCK_TYPE, 0): raise ProtocolError("Invalid END_BLOCK message after block") elif msg_header[1] == self.TRANSACTION_TYPE: if msg_header[2] > self.MAX_RELAY_TRANSACTION_BYTES and (self.recv_transaction_cache.get_flag_count() >= self.MAX_EXTRA_OVERSIZE_TRANSACTIONS or msg_header[2] > self.MAX_RELAY_OVERSIZE_TRANSACTION_BYTES): raise ProtocolError("Got a freely relayed transaction too large (" + str(msg_header[2]) + ") bytes") transaction_data = self.relay_sock.recv(msg_header[2], socket.MSG_WAITALL) self.recv_transaction_cache.add(transaction_data, msg_header[2] > self.MAX_RELAY_OVERSIZE_TRANSACTION_BYTES) self.data_recipient.provide_transaction(transaction_data) if deserialize_utils: transaction = CTransaction.deserialize(transaction_data) print("Got transaction: " + str(b2lx(transaction.GetHash()))) else: print("Got transaction of length " + str(msg_header[2])) elif msg_header[1] == self.MAX_VERSION_TYPE: version = self.relay_sock.recv(msg_header[2], socket.MSG_WAITALL) print("Relay network now uses version " + str(version) + " (PLEASE UPGRADE)") else: raise ProtocolError("Unknown message type: " + str(msg_header[1])) except (OSError, socket.error) as err: print("Lost connect to relay node:", err) self.reconnect() except ProtocolError as err: print("Error processing data from relay node:", err) self.reconnect() except Exception as err: print("Unknown error processing data from relay node:", err) self.reconnect()