Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1#!/usr/bin/env python32# Copyright (c) 2022-present The Bitcoin Core developers3# Distributed under the MIT software license, see the accompanying4# file COPYING or http://www.opensource.org/licenses/mit-license.php.5"""Test the sendall RPC command."""7from decimal import Decimal, getcontext9from test_framework.messages import SEQUENCE_FINAL10from test_framework.test_framework import BitcoinTestFramework11from test_framework.util import (12assert_equal,13assert_greater_than,14assert_greater_than_or_equal,15assert_raises_rpc_error,16)18# Decorator to reset activewallet to zero utxos19def cleanup(func):20def wrapper(self):21func(self)23if 0 < self.wallet.getbalances()["mine"]["trusted"]:24self.wallet.sendall([self.remainder_target])25assert_equal(0, self.wallet.getbalances()["mine"]["trusted"]) # wallet is empty26return wrapper28class SendallTest(BitcoinTestFramework):29def skip_test_if_missing_module(self):30self.skip_if_no_wallet()32def set_test_params(self):33getcontext().prec=1034self.num_nodes = 135self.setup_clean_chain = True37def assert_balance_swept_completely(self, tx, balance):38output_sum = sum([o["value"] for o in tx["decoded"]["vout"]])39assert_equal(output_sum, balance + tx["fee"])40assert_equal(0, self.wallet.getbalances()["mine"]["trusted"]) # wallet is empty42def assert_tx_has_output(self, tx, addr, value=None):43for output in tx["decoded"]["vout"]:44if addr == output["scriptPubKey"]["address"] and value is None or value == output["value"]:45return46raise AssertionError("Output to {} not present or wrong amount".format(addr))48def assert_tx_has_outputs(self, tx, expected_outputs):49assert_equal(len(expected_outputs), len(tx["decoded"]["vout"]))50for eo in expected_outputs:51self.assert_tx_has_output(tx, eo["address"], eo["value"])53def add_utxos(self, amounts):54for a in amounts:55self.def_wallet.sendtoaddress(self.wallet.getnewaddress(), a)56self.generate(self.nodes[0], 1)57assert_greater_than(self.wallet.getbalances()["mine"]["trusted"], 0)58return self.wallet.getbalances()["mine"]["trusted"]60# Helper schema for success cases61def test_sendall_success(self, sendall_args, remaining_balance = 0, *, options=None):62sendall_tx_receipt = self.wallet.sendall(sendall_args, options=options)63self.generate(self.nodes[0], 1)64# wallet has remaining balance (usually empty)65assert_equal(remaining_balance, self.wallet.getbalances()["mine"]["trusted"])67assert_equal(sendall_tx_receipt["complete"], True)68return self.wallet.gettransaction(txid = sendall_tx_receipt["txid"], verbose = True)70@cleanup71def gen_and_clean(self):72self.add_utxos([15, 2, 4])74def test_cleanup(self):75self.log.info("Test that cleanup wrapper empties wallet")76self.gen_and_clean()77assert_equal(0, self.wallet.getbalances()["mine"]["trusted"]) # wallet is empty79# Actual tests80@cleanup81def sendall_two_utxos(self):82self.log.info("Testing basic sendall case without specific amounts")83pre_sendall_balance = self.add_utxos([10,11])84tx_from_wallet = self.test_sendall_success(sendall_args = [self.remainder_target])86self.assert_tx_has_outputs(tx = tx_from_wallet,87expected_outputs = [88{ "address": self.remainder_target, "value": pre_sendall_balance + tx_from_wallet["fee"] } # fee is neg89]90)91self.assert_balance_swept_completely(tx_from_wallet, pre_sendall_balance)93@cleanup94def sendall_split(self):95self.log.info("Testing sendall where two recipients have unspecified amount")96pre_sendall_balance = self.add_utxos([1, 2, 3, 15])97tx_from_wallet = self.test_sendall_success([self.remainder_target, self.split_target])99half = (pre_sendall_balance + tx_from_wallet["fee"]) / 2100self.assert_tx_has_outputs(tx_from_wallet,101expected_outputs = [102{ "address": self.split_target, "value": half },103{ "address": self.remainder_target, "value": half }104]105)106self.assert_balance_swept_completely(tx_from_wallet, pre_sendall_balance)108@cleanup109def sendall_and_spend(self):110self.log.info("Testing sendall in combination with paying specified amount to recipient")111pre_sendall_balance = self.add_utxos([8, 13])112tx_from_wallet = self.test_sendall_success([{self.recipient: 5}, self.remainder_target])114self.assert_tx_has_outputs(tx_from_wallet,115expected_outputs = [116{ "address": self.recipient, "value": 5 },117{ "address": self.remainder_target, "value": pre_sendall_balance - 5 + tx_from_wallet["fee"] }118]119)120self.assert_balance_swept_completely(tx_from_wallet, pre_sendall_balance)122@cleanup123def sendall_invalid_recipient_addresses(self):124self.log.info("Test having only recipient with specified amount, missing recipient with unspecified amount")125self.add_utxos([12, 9])127assert_raises_rpc_error(128-8,129"Must provide at least one address without a specified amount" ,130self.wallet.sendall,131[{self.recipient: 5}]132)134@cleanup135def sendall_duplicate_recipient(self):136self.log.info("Test duplicate destination")137self.add_utxos([1, 8, 3, 9])139assert_raises_rpc_error(140-8,141"Invalid parameter, duplicated address: {}".format(self.remainder_target),142self.wallet.sendall,143[self.remainder_target, self.remainder_target]144)146@cleanup147def sendall_invalid_amounts(self):148self.log.info("Test sending more than balance")149pre_sendall_balance = self.add_utxos([7, 14])151expected_tx = self.wallet.sendall(recipients=[{self.recipient: 5}, self.remainder_target], add_to_wallet=False)152tx = self.wallet.decoderawtransaction(expected_tx['hex'])153fee = 21 - sum([o["value"] for o in tx["vout"]])155assert_raises_rpc_error(-6, "Assigned more value to outputs than available funds.", self.wallet.sendall,156[{self.recipient: pre_sendall_balance + 1}, self.remainder_target])157assert_raises_rpc_error(-6, "Insufficient funds for fees after creating specified outputs.", self.wallet.sendall,158[{self.recipient: pre_sendall_balance}, self.remainder_target])159assert_raises_rpc_error(-8, "Specified output amount to {} is below dust threshold".format(self.recipient),160self.wallet.sendall, [{self.recipient: 0.00000001}, self.remainder_target])161assert_raises_rpc_error(-6, "Dynamically assigned remainder results in dust output.", self.wallet.sendall,162[{self.recipient: pre_sendall_balance - fee}, self.remainder_target])163assert_raises_rpc_error(-6, "Dynamically assigned remainder results in dust output.", self.wallet.sendall,164[{self.recipient: pre_sendall_balance - fee - Decimal(0.00000010)}, self.remainder_target])166# @cleanup not needed because different wallet used167def sendall_negative_effective_value(self):168self.log.info("Test that sendall fails if all UTXOs have negative effective value")169# Use dedicated wallet for dust amounts and unload wallet at end170self.nodes[0].createwallet("dustwallet")171dust_wallet = self.nodes[0].get_wallet_rpc("dustwallet")173self.def_wallet.sendtoaddress(dust_wallet.getnewaddress(), 0.00000400)174self.def_wallet.sendtoaddress(dust_wallet.getnewaddress(), 0.00000300)175self.generate(self.nodes[0], 1)176assert_greater_than(dust_wallet.getbalances()["mine"]["trusted"], 0)178assert_raises_rpc_error(-6, "Total value of UTXO pool too low to pay for transaction."179+ " Try using lower feerate or excluding uneconomic UTXOs with 'send_max' option.",180dust_wallet.sendall, recipients=[self.remainder_target], fee_rate=300)182dust_wallet.unloadwallet()184@cleanup185def sendall_with_send_max(self):186self.log.info("Check that `send_max` option causes negative value UTXOs to be left behind")187self.add_utxos([0.00000400, 0.00000300, 1])189# sendall with send_max190sendall_tx_receipt = self.wallet.sendall(recipients=[self.remainder_target], fee_rate=300, send_max=True)191tx_from_wallet = self.wallet.gettransaction(txid = sendall_tx_receipt["txid"], verbose = True)193assert_equal(len(tx_from_wallet["decoded"]["vin"]), 1)194self.assert_tx_has_outputs(tx_from_wallet, [{"address": self.remainder_target, "value": 1 + tx_from_wallet["fee"]}])195assert_equal(self.wallet.getbalances()["mine"]["trusted"], Decimal("0.00000700"))197self.def_wallet.sendtoaddress(self.wallet.getnewaddress(), 1)198self.generate(self.nodes[0], 1)200@cleanup201def sendall_specific_inputs(self):202self.log.info("Test sendall with a subset of UTXO pool")203self.add_utxos([17, 4])204utxo = self.wallet.listunspent()[0]206sendall_tx_receipt = self.wallet.sendall(recipients=[self.remainder_target], inputs=[utxo])207tx_from_wallet = self.wallet.gettransaction(txid = sendall_tx_receipt["txid"], verbose = True)208assert_equal(len(tx_from_wallet["decoded"]["vin"]), 1)209assert_equal(len(tx_from_wallet["decoded"]["vout"]), 1)210assert_equal(tx_from_wallet["decoded"]["vin"][0]["txid"], utxo["txid"])211assert_equal(tx_from_wallet["decoded"]["vin"][0]["vout"], utxo["vout"])212self.assert_tx_has_output(tx_from_wallet, self.remainder_target)214self.generate(self.nodes[0], 1)215assert_greater_than(self.wallet.getbalances()["mine"]["trusted"], 0)217@cleanup218def sendall_fails_on_missing_input(self):219# fails because UTXO was previously spent, and wallet is empty220self.log.info("Test sendall fails because specified UTXO is not available")221self.add_utxos([16, 5])222spent_utxo = self.wallet.listunspent()[0]224# fails on out of bounds vout225assert_raises_rpc_error(-8,226"Input not found. UTXO ({}:{}) is not part of wallet.".format(spent_utxo["txid"], 1000),227self.wallet.sendall, recipients=[self.remainder_target], inputs=[{"txid": spent_utxo["txid"], "vout": 1000}])229# fails on unconfirmed spent UTXO230self.wallet.sendall(recipients=[self.remainder_target])231assert_raises_rpc_error(-8,232"Input not available. UTXO ({}:{}) was already spent.".format(spent_utxo["txid"], spent_utxo["vout"]),233self.wallet.sendall, recipients=[self.remainder_target], inputs=[spent_utxo])235# fails on specific previously spent UTXO, while other UTXOs exist236self.generate(self.nodes[0], 1)237self.add_utxos([19, 2])238assert_raises_rpc_error(-8,239"Input not available. UTXO ({}:{}) was already spent.".format(spent_utxo["txid"], spent_utxo["vout"]),240self.wallet.sendall, recipients=[self.remainder_target], inputs=[spent_utxo])242# fails because UTXO is unknown, while other UTXOs exist243foreign_utxo = self.def_wallet.listunspent()[0]244assert_raises_rpc_error(-8, "Input not found. UTXO ({}:{}) is not part of wallet.".format(foreign_utxo["txid"],245foreign_utxo["vout"]), self.wallet.sendall, recipients=[self.remainder_target],246inputs=[foreign_utxo])248@cleanup249def sendall_fails_on_no_address(self):250self.log.info("Test sendall fails because no address is provided")
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.