def test_sign_standard_txs(addrtype): # liberally copied from python-bitcoinlib tests, # in particular see: # https://github.com/petertodd/python-bitcoinlib/pull/227 # Create the (in)famous correct brainwallet secret key. priv = hashlib.sha256(b'correct horse battery staple').digest() + b"\x01" pub = btc.privkey_to_pubkey(priv) # Create an address from that private key. # (note that the input utxo is fake so we are really only creating # a destination here). scriptPubKey = btc.CScript([btc.OP_0, btc.Hash160(pub)]) address = btc.P2WPKHCoinAddress.from_scriptPubKey(scriptPubKey) # Create a dummy outpoint; use same 32 bytes for convenience txid = priv[:32] vout = 2 amount = btc.coins_to_satoshi(float('0.12345')) # Calculate an amount for the upcoming new UTXO. Set a high fee to bypass # bitcoind minfee setting. amount_less_fee = int(amount - btc.coins_to_satoshi(0.01)) # Create a destination to send the coins. destination_address = address target_scriptPubKey = scriptPubKey # Create the unsigned transaction. txin = btc.CTxIn(btc.COutPoint(txid[::-1], vout)) txout = btc.CTxOut(amount_less_fee, target_scriptPubKey) tx = btc.CMutableTransaction([txin], [txout]) # Calculate the signature hash for the transaction. This is then signed by the # private key that controls the UTXO being spent here at this txin_index. if addrtype == "p2wpkh": sig, msg = btc.sign(tx, 0, priv, amount=amount, native="p2wpkh") elif addrtype == "p2sh-p2wpkh": sig, msg = btc.sign(tx, 0, priv, amount=amount, native=False) elif addrtype == "p2pkh": sig, msg = btc.sign(tx, 0, priv) else: assert False if not sig: print(msg) raise print("created signature: ", bintohex(sig)) print("serialized transaction: {}".format(bintohex(tx.serialize()))) print("deserialized transaction: {}\n".format( btc.human_readable_transaction(tx)))
def test_spend_then_rbf(setup_tx_creation): """ Test plan: first, create a normal spend with rbf enabled in direct_send, then broadcast but do not mine a block. Then create a re-spend of the same utxos with a higher fee and check that broadcast succeeds. """ # First phase: broadcast with RBF enabled. # # set a baseline feerate: old_feerate = jm_single().config.get("POLICY", "tx_fees") jm_single().config.set("POLICY", "tx_fees", "20000") # set up a single wallet with some coins: wallet_service = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet'] wallet_service.sync_wallet(fast=True) # ensure selection of two utxos, doesn't really matter # but a more general case than only one: amount = 350000000 # destination doesn't matter; this is easiest: destn = wallet_service.get_internal_addr(1) # While `direct_send` usually encapsulates utxo selection # for user, here we need to know what was chosen, hence # we return the transaction object, not directly broadcast. tx1 = direct_send(wallet_service, amount, 0, destn, answeryes=True, return_transaction=True, optin_rbf=True) assert tx1 # record the utxos for reuse: assert isinstance(tx1, bitcoin.CTransaction) utxos_objs = (x.prevout for x in tx1.vin) utxos = [(x.hash[::-1], x.n) for x in utxos_objs] # in order to sign on those utxos, we need their script and value. scrs = {} vals = {} for u, details in wallet_service.get_utxos_by_mixdepth()[0].items(): if u in utxos: scrs[u] = details["script"] vals[u] = details["value"] assert len(scrs.keys()) == 2 assert len(vals.keys()) == 2 # This will go to mempool but not get mined because # we don't call `tick_forward_chain`. push_succeed = jm_single().bc_interface.pushtx(tx1.serialize()) if push_succeed: # mimics real operations with transaction monitor: wallet_service.process_new_tx(tx1) else: assert False # Second phase: bump fee. # # we set a larger fee rate. jm_single().config.set("POLICY", "tx_fees", "30000") # just a different destination to avoid confusion: destn2 = wallet_service.get_internal_addr(2) # We reuse *both* utxos so total fees are comparable # (modulo tiny 1 byte differences in signatures). # Ordinary wallet operations would remove the first-spent utxos, # so for now we build a PSBT using the code from #921 to select # the same utxos (it could be done other ways). # Then we broadcast the PSBT and check it is allowed # before constructing the outputs, we need a good fee estimate, # using the bumped feerate: fee = estimate_tx_fee(2, 2, wallet_service.get_txtype()) # reset the feerate: total_input_val = sum(vals.values()) jm_single().config.set("POLICY", "tx_fees", old_feerate) outs = [{ "address": destn2, "value": 1000000 }, { "address": wallet_service.get_internal_addr(0), "value": total_input_val - 1000000 - fee }] tx2 = bitcoin.mktx(utxos, outs, version=2, locktime=compute_tx_locktime()) spent_outs = [] for u in utxos: spent_outs.append(bitcoin.CTxOut(nValue=vals[u], scriptPubKey=scrs[u])) psbt_unsigned = wallet_service.create_psbt_from_tx(tx2, spent_outs=spent_outs) signresultandpsbt, err = wallet_service.sign_psbt( psbt_unsigned.serialize(), with_sign_result=True) assert not err signresult, psbt_signed = signresultandpsbt tx2_signed = psbt_signed.extract_transaction() # the following assertion is sufficient, because # tx broadcast would fail if the replacement were # not allowed by Core: assert jm_single().bc_interface.pushtx(tx2_signed.serialize())