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.
251self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))252node_2_bal -= Decimal('10')253assert_equal(self.nodes[2].getbalance(), node_2_bal)254node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))256self.log.info("Test sendmany")258# Sendmany 10 BTC259txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [], fee_rate=fee_rate_sat_vb)260self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))261node_0_bal += Decimal('10')262node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))263assert_equal(self.nodes[0].getbalance(), node_0_bal)265# Sendmany 10 BTC with subtract fee from amount266txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [address], fee_rate=fee_rate_sat_vb)267self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))268node_2_bal -= Decimal('10')269assert_equal(self.nodes[2].getbalance(), node_2_bal)270node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))272# Sendmany 5 BTC to two addresses with subtracting fee from both addresses273a0 = self.nodes[0].getnewaddress()274a1 = self.nodes[0].getnewaddress()275txid = self.nodes[2].sendmany(dummy='', amounts={a0: 5, a1: 5}, subtractfeefrom=[a0, a1], fee_rate=fee_rate_sat_vb)276self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))277node_2_bal -= Decimal('10')278assert_equal(self.nodes[2].getbalance(), node_2_bal)279tx = self.nodes[2].gettransaction(txid)280node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, self.get_vsize(tx['hex']))281assert_equal(self.nodes[0].getbalance(), node_0_bal)282expected_bal = Decimal('5') + (tx['fee'] / 2)283assert_equal(self.nodes[0].getreceivedbyaddress(a0), expected_bal)284assert_equal(self.nodes[0].getreceivedbyaddress(a1), expected_bal)286self.log.info("Test sendmany with fee_rate param (explicit fee rate in sat/vB)")287fee_rate_sat_vb = 2288fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8289explicit_fee_rate_btc_kvb = Decimal(fee_rate_btc_kvb) / 1000291# Test passing fee_rate as a string292txid = self.nodes[2].sendmany(amounts={address: 10}, fee_rate=str(fee_rate_sat_vb))293self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))294balance = self.nodes[2].getbalance()295node_2_bal = self.check_fee_amount(balance, node_2_bal - Decimal('10'), explicit_fee_rate_btc_kvb, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))296assert_equal(balance, node_2_bal)297node_0_bal += Decimal('10')298assert_equal(self.nodes[0].getbalance(), node_0_bal)300# Test passing fee_rate as an integer301amount = Decimal("0.0001")302txid = self.nodes[2].sendmany(amounts={address: amount}, fee_rate=fee_rate_sat_vb)303self.generate(self.nodes[2], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))304balance = self.nodes[2].getbalance()305node_2_bal = self.check_fee_amount(balance, node_2_bal - amount, explicit_fee_rate_btc_kvb, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))306assert_equal(balance, node_2_bal)307node_0_bal += amount308assert_equal(self.nodes[0].getbalance(), node_0_bal)310assert_raises_rpc_error(-8, "Unknown named parameter feeRate", self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=1, feeRate=1)312# Test setting explicit fee rate just below the minimum.313self.log.info("Test sendmany raises 'fee rate too low' if fee_rate of 0.99999999 is passed")314assert_raises_rpc_error(-6, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)",315self.nodes[2].sendmany, amounts={address: 10}, fee_rate=0.999)317self.log.info("Test sendmany raises if an invalid fee_rate is passed")318# Test fee_rate with zero values.319msg = "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"320for zero_value in [0, 0.000, 0.00000000, "0", "0.000", "0.00000000"]:321assert_raises_rpc_error(-6, msg, self.nodes[2].sendmany, amounts={address: 1}, fee_rate=zero_value)322msg = "Invalid amount"323# Test fee_rate values that don't pass fixed-point parsing checks.324for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:325assert_raises_rpc_error(-3, msg, self.nodes[2].sendmany, amounts={address: 1.0}, fee_rate=invalid_value)326# Test fee_rate values that cannot be represented in sat/vB.327for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:328assert_raises_rpc_error(-3, msg, self.nodes[2].sendmany, amounts={address: 10}, fee_rate=invalid_value)329# Test fee_rate out of range (negative number).330assert_raises_rpc_error(-3, OUT_OF_RANGE, self.nodes[2].sendmany, amounts={address: 10}, fee_rate=-1)331# Test type error.332for invalid_value in [True, {"foo": "bar"}]:333assert_raises_rpc_error(-3, NOT_A_NUMBER_OR_STRING, self.nodes[2].sendmany, amounts={address: 10}, fee_rate=invalid_value)335self.log.info("Test sendmany raises if an invalid conf_target or estimate_mode is passed")336for target, mode in product([-1, 0, 1009], ["economical", "conservative"]):337assert_raises_rpc_error(-8, "Invalid conf_target, must be between 1 and 1008", # max value of 1008 per src/policy/fees/block_policy_estimator.h338self.nodes[2].sendmany, amounts={address: 1}, conf_target=target, estimate_mode=mode)339for target, mode in product([-1, 0], ["btc/kb", "sat/b"]):340assert_raises_rpc_error(-8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"',341self.nodes[2].sendmany, amounts={address: 1}, conf_target=target, estimate_mode=mode)343self.start_node(3, self.nodes[3].extra_args)344self.connect_nodes(0, 3)345self.sync_all()347# check if we can list zero value tx as available coins348# 1. create raw_tx349# 2. hex-changed one output to 0.0350# 3. sign and send351# 4. check if recipient (node0) can list the zero value tx352usp = self.nodes[1].listunspent(query_options={'minimumAmount': '49.998'})[0]353inputs = [{"txid": usp['txid'], "vout": usp['vout']}]354outputs = {self.nodes[1].getnewaddress(): 49.998, self.nodes[0].getnewaddress(): 11.11}356raw_tx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") # replace 11.11 with 0.0 (int32)357signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(raw_tx)358decoded_raw_tx = self.nodes[1].decoderawtransaction(signed_raw_tx['hex'])359zero_value_txid = decoded_raw_tx['txid']360self.nodes[1].sendrawtransaction(signed_raw_tx['hex'])362self.sync_all()363self.generate(self.nodes[1], 1) # mine a block365unspent_txs = self.nodes[0].listunspent() # zero value tx must be in listunspents output366found = False367for uTx in unspent_txs:368if uTx['txid'] == zero_value_txid:369found = True370assert_equal(uTx['amount'], Decimal('0'))371assert found373self.log.info("Test -walletbroadcast")374self.stop_nodes()375self.start_node(0, ["-walletbroadcast=0"])376self.start_node(1, ["-walletbroadcast=0"])377self.start_node(2, ["-walletbroadcast=0"])378self.connect_nodes(0, 1)379self.connect_nodes(1, 2)380self.connect_nodes(0, 2)381self.sync_all(self.nodes[0:3])383txid_not_broadcast = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)384tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast)385self.generate(self.nodes[1], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3])) # mine a block, tx should not be in there386assert_equal(self.nodes[2].getbalance(), node_2_bal) # should not be changed because tx was not broadcasted388# now broadcast from another node, mine a block, sync, and check the balance389self.nodes[1].sendrawtransaction(tx_obj_not_broadcast['hex'])390self.generate(self.nodes[1], 1, sync_fun=lambda: self.sync_all(self.nodes[0:3]))391node_2_bal += 2392tx_obj_not_broadcast = self.nodes[0].gettransaction(txid_not_broadcast)393assert_equal(self.nodes[2].getbalance(), node_2_bal)395# create another tx396self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2)398# restart the nodes with -walletbroadcast=1399self.stop_nodes()400self.start_node(0)401self.start_node(1)402self.start_node(2)403self.connect_nodes(0, 1)404self.connect_nodes(1, 2)405self.connect_nodes(0, 2)406self.sync_blocks(self.nodes[0:3])408self.generate(self.nodes[0], 1, sync_fun=lambda: self.sync_blocks(self.nodes[0:3]))409node_2_bal += 2411# tx should be added to balance because after restarting the nodes tx should be broadcast412assert_equal(self.nodes[2].getbalance(), node_2_bal)414# send a tx with value in a string (PR#6380 +)415txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "2")416tx_obj = self.nodes[0].gettransaction(txid)417assert_equal(tx_obj['amount'], Decimal('-2'))419txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "0.0001")420tx_obj = self.nodes[0].gettransaction(txid)421assert_equal(tx_obj['amount'], Decimal('-0.0001'))423# check if JSON parser can handle scientific notation in strings424txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), "1e-4")425tx_obj = self.nodes[0].gettransaction(txid)426assert_equal(tx_obj['amount'], Decimal('-0.0001'))428# General checks for errors from incorrect inputs429# This will raise an exception because the amount is negative430assert_raises_rpc_error(-3, OUT_OF_RANGE, self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "-1")432# This will raise an exception because the amount type is wrong433assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4")435# This will raise an exception since generate does not accept a string436assert_raises_rpc_error(-3, "not of expected type number", self.generate, self.nodes[0], "2")438# Mine a block from node0 to an address from node1439coinbase_addr = self.nodes[1].getnewaddress()440block_hash = self.generatetoaddress(self.nodes[0], 1, coinbase_addr, sync_fun=lambda: self.sync_all(self.nodes[0:3]))[0]441coinbase_txid = self.nodes[0].getblock(block_hash)['tx'][0]443# Check that the txid and balance is found by node1444self.nodes[1].gettransaction(coinbase_txid)446# check if wallet or blockchain maintenance changes the balance447self.sync_all(self.nodes[0:3])448blocks = self.generate(self.nodes[0], 2, sync_fun=lambda: self.sync_all(self.nodes[0:3]))449balance_nodes = [self.nodes[i].getbalance() for i in range(3)]450block_count = self.nodes[0].getblockcount()452# Check modes:453# - True: unicode escaped as \u....454# - False: unicode directly as UTF-8455for mode in [True, False]:456self.nodes[0]._rpc.ensure_ascii = mode457# unicode check: Basic Multilingual Plane, Supplementary Plane respectively458for label in [u'рыба', u'𝅘𝅥𝅯']:459addr = self.nodes[0].getnewaddress()460self.nodes[0].setlabel(addr, label)461test_address(self.nodes[0], addr, labels=[label])462assert label in self.nodes[0].listlabels()463self.nodes[0]._rpc.ensure_ascii = True # restore to default465# -reindex tests466chainlimit = 6467self.log.info("Test -reindex")468self.stop_nodes()469# set lower ancestor limit for later470self.start_node(0, ['-reindex', "-walletrejectlongchains=0", "-limitancestorcount=" + str(chainlimit), "-limitclustercount=" + str(chainlimit)])471self.start_node(1, ['-reindex', "-limitclustercount=" + str(chainlimit)])472self.start_node(2, ['-reindex', "-limitclustercount=" + str(chainlimit)])473# reindex will leave rpc warm up "early"; Wait for it to finish474self.wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)])475assert_equal(balance_nodes, [self.nodes[i].getbalance() for i in range(3)])477# Exercise listsinceblock with the last two blocks478coinbase_tx_1 = self.nodes[0].listsinceblock(blocks[0])479assert_equal(coinbase_tx_1["lastblock"], blocks[1])480assert_equal(len(coinbase_tx_1["transactions"]), 1)481assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1])482assert_equal(len(self.nodes[0].listsinceblock(blocks[1])["transactions"]), 0)484# ==Check that wallet prefers to use coins that don't exceed mempool limits =====486# Get all non-zero utxos together and split into two chains487chain_addrs = [self.nodes[0].getnewaddress(), self.nodes[0].getnewaddress()]488self.nodes[0].sendall(recipients=chain_addrs)489self.generate(self.nodes[0], 1, sync_fun=self.no_op)491# Make a long chain of unconfirmed payments without hitting mempool limit492# Each tx we make leaves only one output of change on a chain 1 longer493# Since the amount to send is always much less than the outputs, we only ever need one output494# So we should be able to generate exactly chainlimit txs for each original output495sending_addr = self.nodes[1].getnewaddress()496txid_list = []497for _ in range(chainlimit * 2):498txid_list.append(self.nodes[0].sendtoaddress(sending_addr, Decimal('0.0001')))499assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit * 2)500assert_equal(len(txid_list), chainlimit * 2)
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.