def dummy_op_return_tx(key, message, spendables, fee=10000): address = key.address() if len(message) > 80: raise ValueError("message must not be longer than 80 bytes") message = hexlify(message).decode() bitcoin_sum = sum(spendable.coin_value for spendable in spendables) if bitcoin_sum < fee: raise Exception("not enough bitcoin to cover fee") inputs = [spendable.tx_in() for spendable in spendables] outputs = [] if bitcoin_sum > fee: change_output_script = standard_tx_out_script(address) outputs.append(TxOut(bitcoin_sum - fee, change_output_script)) op_return_output_script = script.tools.compile('OP_RETURN %s' % message) outputs.append(TxOut(0, op_return_output_script)) tx = Tx(version=1, txs_in=inputs, txs_out=outputs) tx.set_unspents(spendables) sign_tx(tx, wifs=[key.wif()]) return tx
def doit(num_ins, num_outs, master_xpub, subpath="0/%d", fee=10000): psbt = BasicPSBT() txn = Tx(2, [], []) # we have a key; use it to provide "plausible" value inputs from pycoin.key.BIP32Node import BIP32Node mk = BIP32Node.from_wallet_key(master_xpub) xfp = mk.fingerprint() psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)] psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)] for i in range(num_ins): # make a fake txn to supply each of the inputs # - each input is 1BTC # addr where the fake money will be stored. subkey = mk.subkey_for_path(subpath % i) sec = subkey.sec() assert len(sec) == 33, "expect compressed" assert subpath[0:2] == '0/' psbt.inputs[i].bip32_paths[sec] = xfp + pack('<II', 0, i) # UTXO that provides the funding for to-be-signed txn supply = Tx(2, [TxIn(pack('4Q', 0xdead, 0xbeef, 0, 0), 73)], []) scr = bytes([0x76, 0xa9, 0x14]) + subkey.hash160() + bytes( [0x88, 0xac]) supply.txs_out.append(TxOut(1E8, scr)) with BytesIO() as fd: supply.stream(fd) psbt.inputs[i].utxo = fd.getvalue() if 0: with BytesIO() as fd: supply.stream(fd, include_witness_data=True) psbt.inputs[i].witness_utxo = fd.getvalue() spendable = TxIn(supply.hash(), 0) txn.txs_in.append(spendable) for i in range(num_outs): # random P2PKH scr = bytes([0x76, 0xa9, 0x14]) + pack( 'I', i + 1) + bytes(16) + bytes([0x88, 0xac]) h = TxOut(round(((1E8 * num_ins) - fee) / num_outs, 4), scr) txn.txs_out.append(h) with BytesIO() as b: txn.stream(b) psbt.txn = b.getvalue() rv = BytesIO() psbt.serialize(rv) assert rv.tell() <= MAX_TXN_LEN, 'too fat' return rv.getvalue()
def op_return_this(privatekey, text, prefix = "KEYSTAMP:", bitcoin_fee = 10000): bitcoin_keyobj = get_key(privatekey) bitcoin_address = bitcoin_keyobj.bitcoin_address() ## Get the spendable outputs we are going to use to pay the fee all_spendables = get_spendables_blockcypher(bitcoin_address) spendables = [] value = 0 for unspent in all_spendables: while value < bitcoin_fee + 10000: coin_value = unspent.get("value") script = h2b(unspent.get("script_hex")) previous_hash = h2b_rev(unspent.get("tx_hash")) previous_index = unspent.get("index") spendables.append(Spendable(coin_value, script, previous_hash, previous_index)) value += coin_value bitcoin_sum = sum(spendable.coin_value for spendable in spendables) if(bitcoin_sum < bitcoin_fee): print "ERROR: not enough balance: available: %s - fee: %s" %(bitcoin_sum, bitcoin_fee) return False ## Create the inputs we are going to use inputs = [spendable.tx_in() for spendable in spendables] ## If we will have change left over create an output to send it back outputs = [] if (bitcoin_sum > bitcoin_fee): change_output_script = standard_tx_out_script(bitcoin_address) total_amout = bitcoin_sum - bitcoin_fee outputs.append(TxOut(total_amout, change_output_script)) # home_address = standard_tx_out_script(bitcoin_address) # #TODO: it needs some love and IQ on input mananagement stuff # outputs.append(TxOut((bitcoin_sum - bitcoin_fee), home_address)) ## Build the OP_RETURN output with our message if prefix is not None and (len(text) + len(prefix) <= 80): text = prefix + text message = hexlify(text.encode()).decode('utf8') op_return_output_script = tools.compile("OP_RETURN %s" % message) outputs.append(TxOut(0, op_return_output_script)) ## Create the transaction and sign it with the private key tx = Tx(version=1, txs_in=inputs, txs_out=outputs) # print tx.as_hex() # print spendables tx.set_unspents(spendables) sign_tx(tx, wifs=[privatekey]) print "singed_tx: %s" %tx.as_hex() #TODO: uncomment this when its ready to push data to blockchian tx_hash = broadcast_tx_blockr(tx.as_hex()) return tx_hash
def doit(num_ins, num_outs, fat=0): psbt = BasicPSBT() txn = Tx(2, [], []) for i in range(num_ins): h = TxIn(pack('4Q', 0, 0, 0, i), i) txn.txs_in.append(h) for i in range(num_outs): # random P2PKH scr = bytes([0x76, 0xa9, 0x14]) + pack( 'I', i + 1) + bytes(16) + bytes([0x88, 0xac]) h = TxOut((1E6 * i) if i else 1E8, scr) txn.txs_out.append(h) with BytesIO() as b: txn.stream(b) psbt.txn = b.getvalue() psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)] psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)] if fat: for i in range(num_ins): psbt.inputs[i].utxo = os.urandom(fat) rv = BytesIO() psbt.serialize(rv) assert rv.tell() <= MAX_TXN_LEN, 'too fat' return rv.getvalue()
def _create_bip69_tx(spendables: List[Spendable], payables: List[Tuple[str, int]], rbf: bool, version: int = 1) -> Tx: spendables.sort(key=lambda utxo: (utxo.as_dict()["tx_hash_hex"], utxo.as_dict()["tx_out_index"])) # Create input list from utxos # Set sequence numbers to zero if using RBF. txs_in = [spendable.tx_in() for spendable in spendables] # type: List[TxIn] if rbf: logging.info("Spending with opt-in Replace by Fee! (RBF)") for txin in txs_in: txin.sequence = 0 # Create output list from payables txs_out = list() # type: List[TxOut] for payable in payables: bitcoin_address, coin_value = payable script = standard_tx_out_script(bitcoin_address) # type: bytes txs_out.append(TxOut(coin_value, script)) txs_out.sort(key=lambda txo: (txo.coin_value, b2h(txo.script))) tx = Tx(version=version, txs_in=txs_in, txs_out=txs_out) # type: Tx tx.set_unspents(spendables) return tx
def nulldata_txout(hexdata): data = binary(hexdata) if len(data) > common.MAX_NULLDATA: raise exceptions.MaxNulldataExceeded(len(data), common.MAX_NULLDATA) script_text = "OP_RETURN %s" % b2h(data) script_bin = tools.compile(script_text) return TxOut(0, script_bin)
def create_raw_transaction(inputs, outputs): coins_sources = [] coins_to = [] total_unspent = 0 total_spent = 0 for intx in inputs: tx_out = TxOut(intx['amount'], h2b(intx['scriptPubKey'])) coins_source = (h2b(intx['txid'])[::-1], intx['vout'], tx_out) coins_sources.append(coins_source) total_unspent += intx['amount'] for outtx in outputs: self.coins_to.append((outtx['amount'], address)) total_spent += outtx['amount'] fee = total_unspent - total_spent if fee < 0: message = "not enough source coins (%s BTC) for destination (%s BTC). Short %s BTC" % ( satoshi_to_btc(total_unspent), satoshi_to_btc(total_spent), satoshi_to_btc(-fee)) raise Exception(message) unsigned_tx = UnsignedTx.standard_tx(coins_from, coins_to) s = io.BytesIO() unsigned_tx.stream(s) tx_bytes = s.getvalue() tx_hex = binascii.hexlify(tx_bytes).decode("utf8") return tx_hex
def generate_return_txouts(self, address_hash160, payload, fee_needed): txs_out = [] script_data = ScriptNulldata(nulldata=payload).script() txs_out.append(TxOut(self.return_fee, script_data)) return txs_out
def hash160data_txout(hexdata, dust_limit=common.DUST_LIMIT): data = binary(hexdata) if len(data) != 20: # 160 bit raise exceptions.InvalidHash160DataSize(len(data)) script_text = "OP_DUP OP_HASH160 %s OP_EQUALVERIFY OP_CHECKSIG" % b2h(data) script_bin = tools.compile(script_text) return TxOut(dust_limit, script_bin)
def check_bip143_tx(self, tx_u_hex, tx_s_hex, txs_out_value_scripthex_pair, tx_in_count, tx_out_count, version, lock_time): tx_u = Tx.from_hex(tx_u_hex) tx_s = Tx.from_hex(tx_s_hex) txs_out = [ TxOut(int(coin_value * 1e8), h2b(script_hex)) for coin_value, script_hex in txs_out_value_scripthex_pair ] for tx in (tx_u, tx_s): self.assertEqual(len(tx.txs_in), tx_in_count) self.assertEqual(len(tx.txs_out), tx_out_count) self.assertEqual(tx.version, version) self.assertEqual(tx.lock_time, lock_time) tx.set_unspents(txs_out) self.check_unsigned(tx_u) self.check_signed(tx_s) tx_hex = tx_u.as_hex() self.assertEqual(tx_hex, tx_u_hex) tx_hex = tx_s.as_hex() self.assertEqual(tx_hex, tx_s_hex) tx_u_prime = self.unsigned_copy(tx_s) tx_hex = tx_u_prime.as_hex() self.assertEqual(tx_hex, tx_u_hex) self.assertEqual(b2h_rev(double_sha256(h2b(tx_s_hex))), tx_s.w_id()) self.assertEqual(b2h_rev(double_sha256(h2b(tx_u_hex))), tx_u.w_id()) self.assertEqual(b2h_rev(double_sha256(h2b(tx_u_hex))), tx_u.id()) return tx_u, tx_s
def test_multisig_one_at_a_time(self): M = 3 N = 3 keys = [ Key(secret_exponent=i, generator=secp256k1_generator) for i in range(1, N + 2) ] tx_in = TxIn.coinbase_tx_in(script=b'') script = script_for_multisig(m=M, sec_keys=[key.sec() for key in keys[:N]]) tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) ids = [ "403e5bfc59e097bb197bf77a692d158dd3a4f7affb4a1fa41072dafe7bec7058", "5931d9995e83721243dca24772d7012afcd4378996a8b953c458175f15a544db", "9bb4421088190bbbb5b42a9eaa9baed7ec7574a407c25f71992ba56ca43d9c44", "03a1dc2a63f93a5cf5a7cb668658eb3fc2eda88c06dc287b85ba3e6aff751771" ] for i in range(1, N + 1): self.assertEqual(tx2.bad_signature_count(), 1) self.assertEqual(tx2.id(), ids[i - 1]) hash160_lookup = build_hash160_lookup( [keys[i - 1].secret_exponent()], [secp256k1_generator]) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.id(), ids[i]) t1 = sorted(who_signed_tx(tx2, 0, UI)) t2 = sorted(((key.address(), SIGHASH_ALL) for key in keys[:i])) self.assertEqual(t1, t2) self.assertEqual(tx2.bad_signature_count(), 0)
def txout(testnet, target_address, value): testnet = flag(testnet) target_address = address(testnet, target_address) value = positive_integer(value) prefix = b'\x6f' if testnet else b"\0" hash160 = b2h(bitcoin_address_to_hash160_sec(target_address, prefix)) script_text = "OP_DUP OP_HASH160 %s OP_EQUALVERIFY OP_CHECKSIG" % hash160 script_bin = tools.compile(script_text) return TxOut(value, script_bin)
def tether_tx(tx_ins, private_key, send_amount, receiver): """ simple usdt transaction here assume utxo comes from the sender and is used for mine fee bitcoin change will be sent back to sender address of course different address's utxo can be used for mine fee, but should be aware sender is determined by the first input in the tx bitcoin change can also be sent back to different address, but should be aware receiver is indicated by the last output address that is not the sender for full customization, use btc sample p2pkh_tx :param tx_ins: utxo from the sender :param private_key: private key of the same sender :param send_amount: (display amount) * (10 ** 8) :param receiver: address to receive usdt """ _txs_in = [] _un_spent = [] total_bal = 0 for tx_id, idx, balance, address in tx_ins: total_bal += balance # must h2b_rev NOT h2b tx_id_b = h2b_rev(tx_id) _txs_in.append(TxIn(tx_id_b, idx)) _un_spent.append(Spendable(balance, standard_tx_out_script(address), tx_id_b, idx)) txn_fee = estimate_p2pkh_tx_bytes(len(tx_ins), 3) * recommend_satoshi_per_byte() _txs_out = [TxOut(total_bal - txn_fee - 546, standard_tx_out_script(tx_ins[0][3])), TxOut(0, binascii.unhexlify(omni_tether_script(send_amount))), TxOut(546, standard_tx_out_script(receiver))] version, lock_time = 1, 0 tx = Tx(version, _txs_in, _txs_out, lock_time) tx.set_unspents(_un_spent) solver = build_hash160_lookup([int(private_key, 16)] * len(tx_ins)) signed_tx = tx.sign(solver, hash_type=SIGHASH_ALL) return signed_tx.as_hex(), signed_tx.id()
def parse_tx_out(self): tx_out = {} tx_out['value'] = self.data_stream.read_int64() tx_out['n'] = len(self.data['vout']) tx_out['scriptPubKey'] = { 'hex': self.data_stream.read_bytes(self.data_stream.read_compact_size()) } self.data['vout'].append(tx_out) new_tx_out = TxOut(tx_out['value'], tx_out['scriptPubKey']['hex']) self.new_txs_out.append(new_tx_out)
def spend_sh_fund(tx_ins, wif_keys, tx_outs): """ spend script hash fund the key point of an input comes from multisig address is that, its sign script is combined with several individual signs :param tx_ins: list with tuple(tx_id, idx, balance, address, redeem_script) :param wif_keys: private keys in wif format, technical should be the same order with the pubkey in redeem script, but pycoin has inner control, so here order is not mandatory :param tx_outs: balance, receiver_address :return: raw hex and tx id """ _txs_in = [] _un_spent = [] for tx_id, idx, balance, address, _ in tx_ins: # must h2b_rev NOT h2b tx_id_b = h2b_rev(tx_id) _txs_in.append(TxIn(tx_id_b, idx)) _un_spent.append( Spendable( balance, script_obj_from_address(address, netcodes=[NET_CODE]).script(), tx_id_b, idx)) _txs_out = [] for balance, receiver_address in tx_outs: _txs_out.append( TxOut( balance, script_obj_from_address(receiver_address, netcodes=[NET_CODE]).script())) version, lock_time = 1, 0 tx = Tx(version, _txs_in, _txs_out, lock_time) tx.set_unspents(_un_spent) # construct hash160_lookup[hash160] = (secret_exponent, public_pair, compressed) for each individual key hash160_lookup = build_hash160_lookup( [Key.from_text(wif_key).secret_exponent() for wif_key in wif_keys]) for i in range(0, len(tx_ins)): # you can add some conditions that if the input script is not p2sh type, not provide p2sh_lookup, # so that all kinds of inputs can work together p2sh_lookup = build_p2sh_lookup([binascii.unhexlify(tx_ins[i][-1])]) tx.sign_tx_in(hash160_lookup, i, tx.unspents[i].script, hash_type=SIGHASH_ALL, p2sh_lookup=p2sh_lookup) return tx.as_hex(), tx.id()
def send_op_return_tx(key, message, fee=10000): """ Send an transaction with an OP_RETURN output. Args: key: the Bitcoin Key to send the transaction from. message: the message to include in OP_RETURN. fee: the miner fee that should be paid in Satoshis. Returns: The broadcasted Tx. """ address = key.address() if len(message) > 80: raise ValueError("message must not be longer than 80 bytes") message = hexlify(message).decode() spendables = spendables_for_address(address) bitcoin_sum = sum(spendable.coin_value for spendable in spendables) if bitcoin_sum < fee: raise Exception("not enough bitcoin to cover fee") inputs = [spendable.tx_in() for spendable in spendables] outputs = [] if bitcoin_sum > fee: change_output_script = standard_tx_out_script(address) outputs.append(TxOut(bitcoin_sum - fee, change_output_script)) op_return_output_script = script.tools.compile('OP_RETURN %s' % message) outputs.append(TxOut(0, op_return_output_script)) tx = Tx(version=1, txs_in=inputs, txs_out=outputs) tx.set_unspents(spendables) sign_tx(tx, wifs=[key.wif()]) broadcast_tx(tx) return tx
def spend_outputs(funding_psbt, finalized_txn, tweaker=None): # take details from PSBT that created a finalized txn (also provided) # and build a new PSBT that spends those change outputs. from pycoin.tx.Tx import Tx from pycoin.tx.TxOut import TxOut from pycoin.tx.TxIn import TxIn funding = Tx.from_bin(finalized_txn) b4 = BasicPSBT().parse(funding_psbt) # segwit change outputs only spendables = [(n, i) for n, i in enumerate(funding.tx_outs_as_spendable()) if i.script[0:2] == b'\x00\x14' and b4.outputs[n].bip32_paths ] #spendables = list(reversed(spendables)) random.shuffle(spendables) if tweaker: tweaker(spendables) nn = BasicPSBT() nn.inputs = [BasicPSBTInput(idx=i) for i in range(len(spendables))] nn.outputs = [BasicPSBTOutput(idx=0)] # copy input values from funding PSBT's output side for p_in, (f_out, sp) in zip(nn.inputs, [(b4.outputs[x], s) for x, s in spendables]): p_in.bip32_paths = f_out.bip32_paths p_in.witness_script = f_out.redeem_script with BytesIO() as fd: sp.stream(fd) p_in.witness_utxo = fd.getvalue() # build new txn: single output, no change, no miner fee act_scr = fake_dest_addr('p2wpkh') dest_out = TxOut(sum(s.coin_value for n, s in spendables), act_scr) txn = Tx(2, [s.tx_in() for _, s in spendables], [dest_out]) # put unsigned TXN into PSBT with BytesIO() as b: txn.stream(b) nn.txn = b.getvalue() with BytesIO() as rv: nn.serialize(rv) raw = rv.getvalue() open('debug/spend_outs.psbt', 'wb').write(raw) return nn, raw
def construct_standard_tx(composed_tx_spec, is_test): txouts = [] STANDARD_SCRIPT_OUT = "OP_DUP OP_HASH160 %s OP_EQUALVERIFY OP_CHECKSIG" for txout in composed_tx_spec.get_txouts(): hash160 = bitcoin_address_to_hash160_sec(txout.target_addr, is_test) script_text = STANDARD_SCRIPT_OUT % b2h(hash160) script_bin = tools.compile(script_text) txouts.append(TxOut(txout.value, script_bin)) txins = [] for cts_txin in composed_tx_spec.get_txins(): txins.append(TxIn(cts_txin.get_txhash(), cts_txin.prevout.n)) version = 1 lock_time = 0 return Tx(version, txins, txouts, lock_time)
async def broadcast(self, tx_hex: str, chg_out: TxOut) -> str: txid = await self.connection.listen_rpc(self.methods["broadcast"], [tx_hex]) # type: str change_address = chg_out.address( netcode=self.chain.netcode) # type:str change_key = self.search_for_key(change_address, change=True) scripthash = self.get_address(change_key) logging.info("Subscribing to new change address...") self.connection.listen_subscribe(self.methods["subscribe"], [scripthash]) logging.info("Finished subscribing to new change address...") return txid
def multisig_M_of_N(self, M, N, unsigned_id, signed_id): keys = [Key(secret_exponent=i) for i in range(1, N+2)] tx_in = TxIn.coinbase_tx_in(script=b'') script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [keys[-1].address()]) self.assertEqual(tx2.id(), unsigned_id) self.assertEqual(tx2.bad_signature_count(), 1) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys) tx2.sign(hash160_lookup=hash160_lookup) self.assertEqual(tx2.id(), signed_id) self.assertEqual(tx2.bad_signature_count(), 0) self.assertEqual(sorted(who_signed.who_signed_tx(tx2, 0)), sorted(((key.address(), SIGHASH_ALL) for key in keys[:M])))
def standard_tx(coins_from, coins_to): txs_in = [] unspents = [] for h, idx, tx_out in coins_from: txs_in.append(TxIn(h, idx)) unspents.append(tx_out) txs_out = [] for coin_value, address in coins_to: txs_out.append(TxOut(coin_value, script_for_address(address))) version, lock_time = 1, 0 tx = Tx(version, txs_in, txs_out, lock_time) tx.set_unspents(unspents) return tx
def test_sign_pay_to_script_multisig(self): M, N = 3, 3 keys = [Key(secret_exponent=i) for i in range(1, N+2)] tx_in = TxIn.coinbase_tx_in(script=b'') underlying_script = ScriptMultisig(m=M, sec_keys=[key.sec() for key in keys[:N]]).script() address = address_for_pay_to_script(underlying_script) self.assertEqual(address, "39qEwuwyb2cAX38MFtrNzvq3KV9hSNov3q") script = standard_tx_out_script(address) tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [address]) hash160_lookup = build_hash160_lookup(key.secret_exponent() for key in keys[:N]) p2sh_lookup = build_p2sh_lookup([underlying_script]) tx2.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) self.assertEqual(tx2.bad_signature_count(), 0) self.assertRaises(who_signed.NoAddressesForScriptTypeError, who_signed.who_signed_tx, tx2, 0)
def test_txout_ordering(txout_small_coin_small_script, txout_large_coin_small_script, txout_small_coin_large_script, txout_large_coin_large_script): a, b = txout_large_coin_large_script, TxOut(10, b"\xFF") assert (a.coin_value, b2h(a.script)) == (b.coin_value, b2h(b.script)) txout_list = [ txout_small_coin_small_script, txout_large_coin_small_script, txout_small_coin_large_script, txout_large_coin_large_script ] random.shuffle(txout_list) txout_list.sort(key=lambda txo: (txo.coin_value, b2h(txo.script))) assert txout_list[0] == txout_small_coin_small_script assert txout_list[1] == txout_small_coin_large_script assert txout_list[2] == txout_large_coin_small_script assert txout_list[3] == txout_large_coin_large_script
def create_change(self, address: str, existing_txouts, balance: int): new_txouts = [] change_size = 100 * 1000000 # 100 coins total_out = sum(txo.coin_value for txo in existing_txouts) balance = balance - total_out spendables = self.get_spendables(address, get_all=True) # Prune out spendables that we will spend. for spendable in spendables.copy(): if total_out == 0: continue if total_out < 0: raise Exception(f"total_out is negative. ({total_out})") if spendable.coin_value > total_out: spendables.remove(spendable) break elif total_out > spendable.coin_value: spendables.remove(spendable) total_out -= spendable.coin_value # XXX: Find a way to cleanly unify this. _, _, address_hash160 = netcode_and_type_for_text(address) # 15 TXs in our own wallet should be a good buffer to have. if len(spendables) >= 15: return [] # Create our payment to ourselves if we still have the coins. if (balance - (change_size * len(spendables))) < change_size: print("Warning: Low balance in address.") balance -= (change_size * len(spendables)) for x in range(len(spendables), 50): balance -= change_size if balance < 0: break script_pay = ScriptPayToAddress(hash160=address_hash160).script() new_txouts.append(TxOut(change_size, script_pay)) return new_txouts
def get_raw_unsigned(self, fee_satoshi): """Return raw transaction ready for signing May return a transaction with amount=0 if the input amount is not enough to cover fees """ txout = self.tx.txs_out[self.vout] amount_satoshi = txout.coin_value if fee_satoshi >= amount_satoshi: logging.warning('Insufficient funds to cover fee') logging.warning('txout has value of {}, fee = {}'.format( amount_satoshi, fee_satoshi)) # Calculate adjusted amount = input amount - fee adjusted_amount_satoshi = max(0, amount_satoshi - fee_satoshi) logging.debug('tx amount = amount - fee = {} - {} = {}'.format( amount_satoshi, fee_satoshi, adjusted_amount_satoshi)) assert adjusted_amount_satoshi >= 0 adjusted_amount_btc = decimal.Decimal( adjusted_amount_satoshi) / satoshi_per_btc logging.debug("Create tx: {} sat -> {}".format(adjusted_amount_satoshi, self.dest_address)) logging.info("Input tx id = {}, vout={}".format( self.tx.id().encode("ascii"), self.vout)) txin = TxIn(self.tx.hash(), self.vout, sequence=MAX_BIP125_RBF_SEQUENCE) scriptPubKey = pycoin.ui.script_obj_from_address(self.dest_address) txout = TxOut(adjusted_amount_satoshi, scriptPubKey.script()) # Set nlocktime to the current blockheight to discourage 'fee sniping', as per the core # wallet implementation nlocktime = util.get_current_blockcount() or 0 version = 1 tx = Tx(version, [ txin, ], [ txout, ], nlocktime) return tx.as_hex()
def _create_replacement_tx(self, hist_obj: History, version: int = 1) -> Tuple[Tx, Set[str], int]: """ Builds a replacement Bitcoin transaction based on a given History object in order to implement opt in Replace-By-Fee. :param hist_obj: a History object from our tx history data :param version: an int representing the Tx version :returns: A not-fully-formed and unsigned replacement Tx object :raise: Raises a ValueError if tx not a spend or is already confirmed """ if hist_obj.height == 0 and hist_obj.is_spend: old_tx = hist_obj.tx_obj # type: Tx spendables = old_tx.unspents # type: List[Spendable] chg_vout = None # type: int in_addrs = set() # type: Set[str] for utxo in spendables: in_addrs.add(utxo.address(self.chain.netcode)) txs_out = list() # type: List[TxOut] for i, txout in enumerate(old_tx.txs_out): value = None # type: int if txout.coin_value / Wallet.COIN == hist_obj.value: value = txout.coin_value else: value = 0 chg_vout = i txs_out.append(TxOut(value, txout.script)) new_tx = Tx(version=version, txs_in=old_tx.txs_in, txs_out=txs_out) # type: Tx new_tx.set_unspents(spendables) return new_tx, in_addrs, chg_vout else: raise ValueError("This transaction is not replaceable")
def test_sign_pay_to_script_multisig(self): M, N = 3, 3 keys = [ Key(secret_exponent=i, generator=secp256k1_generator) for i in range(1, N + 2) ] tx_in = TxIn.coinbase_tx_in(script=b'') underlying_script = script_for_multisig( m=M, sec_keys=[key.sec() for key in keys[:N]]) address = address_for_p2s(underlying_script) self.assertEqual(address, "39qEwuwyb2cAX38MFtrNzvq3KV9hSNov3q") script = script_for_address(address) tx_out = TxOut(1000000, script) tx1 = Tx(version=1, txs_in=[tx_in], txs_out=[tx_out]) tx2 = tx_utils.create_tx(tx1.tx_outs_as_spendable(), [address]) hash160_lookup = build_hash160_lookup( (key.secret_exponent() for key in keys[:N]), [secp256k1_generator]) p2sh_lookup = build_p2sh_lookup([underlying_script]) tx2.sign(hash160_lookup=hash160_lookup, p2sh_lookup=p2sh_lookup) self.assertEqual(tx2.bad_signature_count(), 0) self.assertEqual( sorted(who_signed_tx(tx2, 0, UI)), sorted(((key.address(), SIGHASH_ALL) for key in keys[:M])))
def generate_addr_txouts(self, address_hash160, _payload, fee_needed): txs_out = [] payload = io.BytesIO(_payload) # XXX: constants file payload_size = 20 # Create all the payments to the data addresses. # Chunking from https://github.com/vilhelmgray/FamaMonetae/blob/master/famamonetae.py while True: chunk = payload.read(payload_size) chunk_size = len(chunk) # Break once our chunk is smaller than the payload size if chunk_size < payload_size: if chunk_size == 0: break chunk = chunk + (B'\x00') * (payload_size - chunk_size) script_data = ScriptPayToAddress(hash160=chunk).script() txs_out.append(TxOut(self.return_fee, script_data)) return txs_out
def spend_pkh_fund(tx_ins, in_keys, tx_outs): """ p2pkh address send to p2pkh p2sh transaction :param tx_ins: list with tuple(tx_id, idx, balance, address) :param in_keys: list of private keys in hex format corresponding to each input :param tx_outs: balance, receiver_address :return: raw hex and tx id """ _txs_in = [] _un_spent = [] for tx_id, idx, balance, address in tx_ins: # must h2b_rev NOT h2b tx_id_b = h2b_rev(tx_id) _txs_in.append(TxIn(tx_id_b, idx)) _un_spent.append( Spendable( balance, script_obj_from_address(address, netcodes=[NET_CODE]).script(), tx_id_b, idx)) _txs_out = [] for balance, receiver_address in tx_outs: _txs_out.append( TxOut( balance, script_obj_from_address(receiver_address, netcodes=[NET_CODE]).script())) version, lock_time = 1, 0 tx = Tx(version, _txs_in, _txs_out, lock_time) tx.set_unspents(_un_spent) solver = build_hash160_lookup([int(pri_hex, 16) for pri_hex in in_keys]) tx.sign(solver, hash_type=SIGHASH_ALL) return tx.as_hex(), tx.id()
def _create_bip69_tx(spendables: List[Spendable], payables: List[Tuple[str, int]], rbf: bool, version: int = 1) -> Tx: """ Create tx inputs and outputs from spendables and payables. Sort lexicographically and return unsigned Tx object. :param spendables: A list of Spendable objects :param payables: A list of payable tuples :param rbf: Replace by fee flag :param version: Tx format version :returns: Fully formed but unsigned Tx object """ spendables.sort(key=lambda utxo: (utxo.as_dict()["tx_hash_hex"], utxo.as_dict()["tx_out_index"])) # Create input list from utxos # Set sequence numbers to zero if using RBF. txs_in = [spendable.tx_in() for spendable in spendables] # type: List[TxIn] if rbf: logging.info("Spending with opt-in Replace by Fee! (RBF)") for txin in txs_in: txin.sequence = 0 # Create output list from payables txs_out = [] # type: List[TxOut] for payable in payables: bitcoin_address, coin_value = payable script = standard_tx_out_script(bitcoin_address) # type: bytes txs_out.append(TxOut(coin_value, script)) txs_out.sort(key=lambda txo: (txo.coin_value, b2h(txo.script))) tx = Tx(version=version, txs_in=txs_in, txs_out=txs_out) # type: Tx tx.set_unspents(spendables) return tx