From f50cbcc6e7fce11de24ed5587b217b85bcef6751 Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Thu, 23 Oct 2025 23:08:46 +0300 Subject: [PATCH 1/8] Begin to document protocol v1.6 changes - new method: mempool.get_info - changed method: blockchain.estimatefee Also added sections discussing the mempool transaction ordering specification. --- conf.py | 2 +- protocol-basics.rst | 17 +++++++++++++++ protocol-changes.rst | 13 +++++++++++ protocol-methods.rst | 51 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/conf.py b/conf.py index e0aec0e..dd87960 100644 --- a/conf.py +++ b/conf.py @@ -58,7 +58,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. diff --git a/protocol-basics.rst b/protocol-basics.rst index 0e86c22..00484a6 100644 --- a/protocol-basics.rst +++ b/protocol-basics.rst @@ -371,3 +371,20 @@ serialized input. So for example if the transaction's input serialized and hashe Then possible prefixes would be either: :const:`"a"` (4 bit prefix of the above hash), :const:`"ab"` (8 bit prefix of the above hash), :const:`"abc"` (12 bit prefix of the above hash), or :const:`"abcd"` (16 bit prefix of the above hash). + + +.. _mempoolorder: + +Mempool Transaction Ordering +---------------------------- + +Certain methods, such as :func:`blockchain.scripthash.get_mempool` and :func:`blockchain.scripthash.get_history` may +return a list of transactions from the mempool. The mempool ordering in this protocol is specified as follows: they are +sorted in ascending order using the following pseudo-code comparator function (where :const:`a` and :const:`b` are +transactions to be compared):: + + (a.hasUnconfirmedParents, a.txHash) < (b.hasUnconfirmedParents, b.txHash) + +That is, transactions are sorted such that all transactions with no unconfirmed parents appear as a grouping before +all transactions with unconfirmed parents, and within each grouping, they are sorted by their transaction hash +(as a string). diff --git a/protocol-changes.rst b/protocol-changes.rst index 857f4df..93f9085 100644 --- a/protocol-changes.rst +++ b/protocol-changes.rst @@ -286,3 +286,16 @@ New methods * :func:`blockchain.rpa.get_history` and :func:`blockchain.rpa.get_mempool` to retrieve transactions matching a certain RPA (reusable payment address) prefix. * :func:`blockchain.reusable.get_history` and :func:`blockchain.reusable.get_mempool` which are compatibility-versions of the above for legacy clients. * :func:`server.features` added a new optional key, :const:`"rpa"`. + +Version 1.6.0 +============= + +Changes +------- + +* :func:`blockchain.estimatefee` changed to allow for an optional second argument, :const:`"mode"`. + +New methods +----------- + +* :func:`mempool.get_info` to retrieve mempool statistics as reported by the bitcoin daemon. diff --git a/protocol-methods.rst b/protocol-methods.rst index 58c0302..d84ece0 100644 --- a/protocol-methods.rst +++ b/protocol-methods.rst @@ -412,12 +412,21 @@ be confirmed within a certain number of blocks. **Signature** - .. function:: blockchain.estimatefee(number) + .. function:: blockchain.estimatefee(number, [mode]) + .. versionchanged:: 1.6.0 + Added optional arg: *mode* (BTC and LTC only) *number* The number of blocks to target for confirmation. + *mode* (optional, BTC & LTC only) + + If the remote bitcoind supports this argument, the estimation mode to use, as a string. This string is + daemon-specific, but for example on BTC and LTC if can be one of: :const:`"unset"`, :const:`"economical"`, + :const:`"conservative"`. If unspecified, the default for the remote bitcoind is used (usually: + :const:`"conservative"`). + **Result** The estimated transaction fee in coin units per kilobyte, as a @@ -962,7 +971,7 @@ hashes>`. **Result** - A list of mempool transactions in arbitrary order. Each mempool + A list of mempool transactions in a :ref:`specified order `. Each mempool transaction is a dictionary with the following keys: * *height* @@ -1800,6 +1809,44 @@ pool, weighted by transaction size. [[12, 128812], [4, 92524], [2, 6478638], [1, 22890421]] +mempool.get_info +================ + +Returns a dictionary containing various mempool stats obtained from the bitcoin daemon's :const:`"getmempoolinfo"` RPC. + +**Signature** + + .. function:: mempool.get_info() + .. versionadded:: 1.6 + +**Result** + + The response is a dictionary that contains daemon-specific values. The following keys *may* be found in the response: + :const:`"mempoolminfee"`, :const:`"minrelaytxfee"`, :const:`"incrementalrelayfee"`, :const:`"unbroadcastcount"`, + and :const:`"fullrbf"`. However, on BCH for example, some of the preceding keys may be be missing altogether since the + bitcoin daemon does have the same BTC-specific mempool concepts related to fees and as a result does not provide + some of these keys via the :const:`"getmempoolinfo"` RPC. + +**Example Result** + + :: + + // On BCH's Bitcoin Cash Node: + { + "mempoolminfee": 0.00001000, + "minrelaytxfee": 0.00001000 + } + + // On BTC's Bitcoin Core: + { + "mempoolminfee": 0.00001000, + "minrelaytxfee": 0.00001000, + "incrementalrelayfee": 0.00001000, + "unbroadcastcount": 5, + "fullrbf": true + } + + server.add_peer =============== From 83b91cb0c7676ce12712065990113d5b31b54e2a Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Thu, 23 Oct 2025 23:32:04 +0300 Subject: [PATCH 2/8] Update document version to 1.6.0, clarify mempool ordering - Updated copyright year - Updated protocol version to 1.6.0 in document - Clarified mempool ordering description - Refer to mempool ordering properly from the "status" section. --- conf.py | 4 ++-- protocol-basics.rst | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/conf.py b/conf.py index dd87960..3464962 100644 --- a/conf.py +++ b/conf.py @@ -15,12 +15,12 @@ import os import sys sys.path.insert(0, os.path.abspath('..')) -VERSION="Fulcrum Protocol Reference 1.5.1" +VERSION="Fulcrum Protocol Reference 1.6.0" # -- Project information ----------------------------------------------------- project = 'Electrum Cash Protocol' -copyright = '2016-2019, Neil Booth, 2020-2024, Calin Culianu' +copyright = '2016-2019, Neil Booth, 2020-2025, Calin Culianu' author = 'Neil Booth, Calin Culianu, Electrum Cash protocol devs' # The full version including branding diff --git a/protocol-basics.rst b/protocol-basics.rst index 00484a6..8663480 100644 --- a/protocol-basics.rst +++ b/protocol-basics.rst @@ -154,7 +154,7 @@ block) * ``height`` is the height of the block it is in. -3. Next, with mempool transactions in any order, append a similar +3. Next, with mempool transactions in :ref:`a specified order `, append a similar string for those transactions, but where **height** is ``-1`` if the transaction has at least one unconfirmed input, and ``0`` if all inputs are confirmed. @@ -378,13 +378,17 @@ Then possible prefixes would be either: :const:`"a"` (4 bit prefix of the above Mempool Transaction Ordering ---------------------------- -Certain methods, such as :func:`blockchain.scripthash.get_mempool` and :func:`blockchain.scripthash.get_history` may -return a list of transactions from the mempool. The mempool ordering in this protocol is specified as follows: they are -sorted in ascending order using the following pseudo-code comparator function (where :const:`a` and :const:`b` are +The protocol specifies a specific ordering for mempool transactions. This ordering is not necessarily topological +ordering, but is something simpler. The mempool ordering in this protocol is specified as follows: mempool transactions +are sorted in ascending order using the following pseudo-code comparator function (where :const:`a` and :const:`b` are transactions to be compared):: (a.hasUnconfirmedParents, a.txHash) < (b.hasUnconfirmedParents, b.txHash) That is, transactions are sorted such that all transactions with no unconfirmed parents appear as a grouping before -all transactions with unconfirmed parents, and within each grouping, they are sorted by their transaction hash -(as a string). +all transactions with unconfirmed parents, and within each grouping, they are sorted by their transaction hash (as a +string). + +Certain concepts in the protocol such as the :ref:`status hash ` make use of this ordering. Additionally, +certain methods, such as :func:`blockchain.scripthash.get_mempool` and :func:`blockchain.scripthash.get_history` may +return a list of transactions from the mempool in this order. From 4162b7c11f17ecf49c5913fdfb9cb2f8c4aaa1ab Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Fri, 24 Oct 2025 00:03:16 +0300 Subject: [PATCH 3/8] Various tweaks, refer to mempool ordering in more places, various nits, etc --- protocol-basics.rst | 2 +- protocol-changes.rst | 30 +++++++++++++++++++----------- protocol-methods.rst | 30 ++++++++++++++++++++++++++---- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/protocol-basics.rst b/protocol-basics.rst index 8663480..15bd033 100644 --- a/protocol-basics.rst +++ b/protocol-basics.rst @@ -154,7 +154,7 @@ block) * ``height`` is the height of the block it is in. -3. Next, with mempool transactions in :ref:`a specified order `, append a similar +3. Next, with mempool transactions in a :ref:`specified order `, append a similar string for those transactions, but where **height** is ``-1`` if the transaction has at least one unconfirmed input, and ``0`` if all inputs are confirmed. diff --git a/protocol-changes.rst b/protocol-changes.rst index 93f9085..9cbaacf 100644 --- a/protocol-changes.rst +++ b/protocol-changes.rst @@ -270,12 +270,12 @@ Version 1.5.2 New methods ----------- -* :func:`blockchain.header.get` to retrieve a block header by its hash. -* :func:`blockchain.scripthash.get_first_use` and :func:`blockchain.address.get_first_use` to retrieve - the first occurrence of a script hash or address on the block chain. -* :func:`blockchain.transaction.get_confirmed_blockhash` to retrieve the block hash of the block that - contains a particular transaction. -* :func:`daemon.passthrough` to forward RPC requests directly through to the bitcoin daemon (disabled by default). + * :func:`blockchain.header.get` to retrieve a block header by its hash. + * :func:`blockchain.scripthash.get_first_use` and :func:`blockchain.address.get_first_use` to retrieve + the first occurrence of a script hash or address on the block chain. + * :func:`blockchain.transaction.get_confirmed_blockhash` to retrieve the block hash of the block that + contains a particular transaction. + * :func:`daemon.passthrough` to forward RPC requests directly through to the bitcoin daemon (disabled by default). Version 1.5.3 ============= @@ -283,9 +283,9 @@ Version 1.5.3 New methods ----------- -* :func:`blockchain.rpa.get_history` and :func:`blockchain.rpa.get_mempool` to retrieve transactions matching a certain RPA (reusable payment address) prefix. -* :func:`blockchain.reusable.get_history` and :func:`blockchain.reusable.get_mempool` which are compatibility-versions of the above for legacy clients. -* :func:`server.features` added a new optional key, :const:`"rpa"`. + * :func:`blockchain.rpa.get_history` and :func:`blockchain.rpa.get_mempool` to retrieve transactions matching a certain RPA (reusable payment address) prefix. + * :func:`blockchain.reusable.get_history` and :func:`blockchain.reusable.get_mempool` which are compatibility-versions of the above for legacy clients. + * :func:`server.features` added a new optional key, :const:`"rpa"`. Version 1.6.0 ============= @@ -293,9 +293,17 @@ Version 1.6.0 Changes ------- -* :func:`blockchain.estimatefee` changed to allow for an optional second argument, :const:`"mode"`. + * :func:`blockchain.estimatefee` changed to allow for an optional second argument, :const:`"mode"`. + * :func:`blockchain.scripthash.get_mempool` previously did not define an order for mempool transactions. We now + mandate a :ref:`specific ordering `. New methods ----------- -* :func:`mempool.get_info` to retrieve mempool statistics as reported by the bitcoin daemon. + * :func:`mempool.get_info` to get more detailed and general relayfee info. + +Deprecated methods +------------------ + + * :func:`blockchain.relayfee`. Switch to :func:`mempool.get_info`. + diff --git a/protocol-methods.rst b/protocol-methods.rst index d84ece0..88e7b6d 100644 --- a/protocol-methods.rst +++ b/protocol-methods.rst @@ -104,6 +104,8 @@ Return the unconfirmed transactions of a Bitcoin Cash address. .. function:: blockchain.address.get_mempool(address) .. versionadded:: 1.4.3 + .. versionchanged:: 1.6 + results must be :ref:`sorted ` (previously unspecified order) * *address* @@ -581,8 +583,15 @@ that is to say: unsubscribe from receiving block header notifications. blockchain.relayfee =================== -Return the minimum fee a low-priority transaction must pay in order to -be accepted to the daemon's memory pool. +Feerates lower than this are considered zero fee and are not being relayed to +the bitcoin network by the server. This feerate does not guarantee acceptance +into the mempool of the server. To get an accurate idea of the current dynamic +minimum relay fee, use the :func:`mempool.get_info` method instead. + +**Deprecated** + + This function is provided for compatibility with existing clients. Please use :func:`mempool.get_info` in new code + instead. **Signature** @@ -590,7 +599,7 @@ be accepted to the daemon's memory pool. **Result** - The fee in whole coin units (BTC, not satoshis for Bitcoin) as a + The fee in whole coin units (BTC/BCH/LTC units, not satoshis for Bitcoin) as a floating point number. **Example Results** @@ -637,6 +646,8 @@ Return the unconfirmed (mempool) transactions for an :ref:`rpa_prefix ` (previously unspecified order) **Availability** @@ -717,6 +728,8 @@ Return the unconfirmed (mempool) transactions for an :ref:`rpa_prefix ` (previously unspecified order) *rpa_prefix* @@ -732,7 +745,8 @@ Return the unconfirmed (mempool) transactions for an :ref:`rpa_prefix `. Each unconfirmed transaction is a + dictionary with the following keys: * *height* @@ -964,6 +978,8 @@ hashes>`. .. function:: blockchain.scripthash.get_mempool(scripthash) .. versionadded:: 1.1 + .. versionchanged:: 1.6 + results must be :ref:`sorted ` (previously unspecified order) *scripthash* @@ -1827,6 +1843,12 @@ Returns a dictionary containing various mempool stats obtained from the bitcoin bitcoin daemon does have the same BTC-specific mempool concepts related to fees and as a result does not provide some of these keys via the :const:`"getmempoolinfo"` RPC. + Of note is the following key: + + * :const:`"mempoolminfee"` + * Type: floating point number + * Value: Dynamic minimum fee rate (in BCH/kB, BTC/kvB, or LTC/kvB) for a tx to be accepted given current conditions. + **Example Result** :: From 66b59108a8f9bd8ebe4f5683b57fd921c055b001 Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Fri, 24 Oct 2025 00:30:47 +0300 Subject: [PATCH 4/8] Added .gitignore for .build/ --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..69fa449 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +_build/ From 81363c487b053da7306cf7a202e9650be2766019 Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Fri, 24 Oct 2025 08:53:41 +0300 Subject: [PATCH 5/8] Document blockchain.block.headers change for 1.6; more misc doc tweaks --- protocol-basics.rst | 8 ++--- protocol-changes.rst | 22 +++++--------- protocol-methods.rst | 71 ++++++++++++++++++++++++++++---------------- 3 files changed, 57 insertions(+), 44 deletions(-) diff --git a/protocol-basics.rst b/protocol-basics.rst index 15bd033..fe6c095 100644 --- a/protocol-basics.rst +++ b/protocol-basics.rst @@ -11,7 +11,7 @@ Two standards `JSON RPC 1.0 `_ and `JSON RPC 2.0 `_ are specified; use of version 2.0 is encouraged but not required. Server support for batch requests -*is* implemented in Fulcrum as of version 1.6.0. +*is* implemented in Fulcrum as of Fulcrum version 1.6.0. .. note:: A client or server should only indicate JSON RPC 2.0 by @@ -154,7 +154,7 @@ block) * ``height`` is the height of the block it is in. -3. Next, with mempool transactions in a :ref:`specified order `, append a similar +3. Next, with mempool transactions in a :ref:`canonical order `, append a similar string for those transactions, but where **height** is ``-1`` if the transaction has at least one unconfirmed input, and ``0`` if all inputs are confirmed. @@ -378,10 +378,10 @@ Then possible prefixes would be either: :const:`"a"` (4 bit prefix of the above Mempool Transaction Ordering ---------------------------- -The protocol specifies a specific ordering for mempool transactions. This ordering is not necessarily topological +The protocol specifies a canonical ordering for mempool transactions. This ordering is not necessarily topological ordering, but is something simpler. The mempool ordering in this protocol is specified as follows: mempool transactions are sorted in ascending order using the following pseudo-code comparator function (where :const:`a` and :const:`b` are -transactions to be compared):: +mempool transactions to be compared):: (a.hasUnconfirmedParents, a.txHash) < (b.hasUnconfirmedParents, b.txHash) diff --git a/protocol-changes.rst b/protocol-changes.rst index 9cbaacf..a4d348a 100644 --- a/protocol-changes.rst +++ b/protocol-changes.rst @@ -2,7 +2,7 @@ Protocol Changes ================ -This documents lists changes made by protocol version. +This document lists changes made by protocol version. Version 1.0 =========== @@ -150,18 +150,6 @@ Removed methods Version 1.4.1 ============= -Changes -------- - - * :func:`blockchain.block.header` and :func:`blockchain.block.headers` now - truncate AuxPoW data (if using an AuxPoW chain) when *cp_height* is - nonzero. AuxPoW data is still present when *cp_height* is zero. - Non-AuxPoW chains are unaffected. - - -Version 1.4.1 -============= - New methods ----------- @@ -293,9 +281,13 @@ Version 1.6.0 Changes ------- + * The status of a scripthash has its definition tightened in a backwards-compatible way: mempool txs now have a + :ref:`canonical ordering ` specified for the calculation (previously their order was unspecified). * :func:`blockchain.estimatefee` changed to allow for an optional second argument, :const:`"mode"`. - * :func:`blockchain.scripthash.get_mempool` previously did not define an order for mempool transactions. We now - mandate a :ref:`specific ordering `. + * :func:`blockchain.scripthash.get_mempool`, :func:`blockchain.scripthash.get_history`, + :func:`blockchain.address.get_mempool`, and :func:`blockchain.address.get_history` previously did not define an + order for mempool transactions. We now mandate a :ref:`canonical ordering `. + * :func:`blockchain.block.headers` now returns headers as a list, instead of a single concatenated hex string New methods ----------- diff --git a/protocol-methods.rst b/protocol-methods.rst index 88e7b6d..b089c1d 100644 --- a/protocol-methods.rst +++ b/protocol-methods.rst @@ -69,6 +69,8 @@ Return the confirmed and unconfirmed history of a Bitcoin Cash address. .. versionadded:: 1.4.3 .. versionchanged:: 1.5.1 Allow pagination of history (added from_height, to_height) + .. versionchanged:: 1.6.0 + results must be :ref:`sorted ` (previously unspecified order) * *address* @@ -104,7 +106,7 @@ Return the unconfirmed transactions of a Bitcoin Cash address. .. function:: blockchain.address.get_mempool(address) .. versionadded:: 1.4.3 - .. versionchanged:: 1.6 + .. versionchanged:: 1.6.0 results must be :ref:`sorted ` (previously unspecified order) * *address* @@ -268,7 +270,6 @@ Return the block header at the given height. .. versionadded:: 1.3 .. versionchanged:: 1.4 *cp_height* parameter added - .. versionchanged:: 1.4.1 *height* @@ -318,11 +319,11 @@ With *cp_height* 8:: { "branch": [ - "000000004ebadb55ee9096c9a2f8880e09da59c0d68b1c228da88e48844a1485", - "96cbbc84783888e4cc971ae8acf86dd3c1a419370336bb3c634c97695a8c5ac9", - "965ac94082cebbcffe458075651e9cc33ce703ab0115c72d9e8b1a9906b2b636", - "89e5daa6950b895190716dd26054432b564ccdc2868188ba1da76de8e1dc7591" - ], + "000000004ebadb55ee9096c9a2f8880e09da59c0d68b1c228da88e48844a1485", + "96cbbc84783888e4cc971ae8acf86dd3c1a419370336bb3c634c97695a8c5ac9", + "965ac94082cebbcffe458075651e9cc33ce703ab0115c72d9e8b1a9906b2b636", + "89e5daa6950b895190716dd26054432b564ccdc2868188ba1da76de8e1dc7591" + ], "header": "0100000085144a84488ea88d221c8bd6c059da090e88f8a2c99690ee55dbba4e00000000e11c48fecdd9e72510ca84f023370c9a38bf91ac5cae88019bee94d24528526344c36649ffff001d1d03e477", "root": "e347b1c43fd9b5415bf0d92708db8284b78daf4d0e24f9c3405f45feb85e25db" } @@ -330,7 +331,7 @@ With *cp_height* 8:: blockchain.block.headers ======================== -Return a concatenated chunk of block headers from the main chain. +Return a number of consecutive block headers from the main chain. **Signature** @@ -338,7 +339,8 @@ Return a concatenated chunk of block headers from the main chain. .. versionadded:: 1.2 .. versionchanged:: 1.4 *cp_height* parameter added - .. versionchanged:: 1.4.1 + .. versionchanged:: 1.6.0 + response contains *headers* field instead of *hex* *start_height* @@ -366,11 +368,15 @@ Return a concatenated chunk of block headers from the main chain. the available headers will be returned. If more headers than *max* were requested at most *max* will be returned. - * *hex* + * *hex* (protocol version < 1.6.0 only) The binary block headers concatenated together in-order as a - hexadecimal string. Starting with version 1.4.1, AuxPoW data (if present - in the original header) is truncated if *cp_height* is nonzero. + hexadecimal string. + + * *headers* (protocol version >= 1.6.0) + + An array containing the binary block headers in-order; each header is a + hexadecimal string. * *max* @@ -396,13 +402,27 @@ Return a concatenated chunk of block headers from the main chain. **Example Response** See :ref:`here ` for an example of *root* and -*branch* keys. +*branch* keys (protocol < 1.6.0). + +:: + + { + "count": 2, + "hex": "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299", + "max": 2016 + } + +See :ref:`here ` for an example of *root* and +*branch* keys (protocol >= 1.6.0). :: { "count": 2, - "hex": "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299" + "headers": [ + "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c", + "010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299" + ], "max": 2016 } @@ -646,7 +666,7 @@ Return the unconfirmed (mempool) transactions for an :ref:`rpa_prefix ` (previously unspecified order) **Availability** @@ -728,7 +748,7 @@ Return the unconfirmed (mempool) transactions for an :ref:`rpa_prefix ` (previously unspecified order) *rpa_prefix* @@ -745,7 +765,7 @@ Return the unconfirmed (mempool) transactions for an :ref:`rpa_prefix `. Each unconfirmed transaction is a + A list of unconfirmed transactions in a :ref:`canonical order `. Each unconfirmed transaction is a dictionary with the following keys: * *height* @@ -902,6 +922,8 @@ Return the confirmed and unconfirmed history of a :ref:`script hash .. versionadded:: 1.1 .. versionchanged:: 1.5.1 Allow pagination of history (added from_height, to_height) + .. versionchanged:: 1.6.0 + Any mempool transactions appearing at the end must be :ref:`sorted ` (previously unspecified order) *scripthash* @@ -924,7 +946,7 @@ Return the confirmed and unconfirmed history of a :ref:`script hash **Result** A list of confirmed transactions in blockchain order, with the - output of :func:`blockchain.scripthash.get_mempool` appended to the + :ref:`sorted ` output of :func:`blockchain.scripthash.get_mempool` appended to the list (mempool is appended only if ``to_height`` is ``-1``). Each confirmed transaction is a dictionary with the following keys: @@ -936,12 +958,11 @@ Return the confirmed and unconfirmed history of a :ref:`script hash The transaction hash in hexadecimal. - Note that as of Fulcrum 1.8.0 the transaction history for a script hash + Note that as of Fulcrum version 1.8.0 the transaction history for a script hash also includes transactions that involve sending/receiving :ref:`CashTokens ` to/from that script hash. - See :func:`blockchain.scripthash.get_mempool` for how mempool - transactions are returned. + See :func:`blockchain.scripthash.get_mempool` for how mempool transactions are returned. **Result Examples** @@ -978,7 +999,7 @@ hashes>`. .. function:: blockchain.scripthash.get_mempool(scripthash) .. versionadded:: 1.1 - .. versionchanged:: 1.6 + .. versionchanged:: 1.6.0 results must be :ref:`sorted ` (previously unspecified order) *scripthash* @@ -987,7 +1008,7 @@ hashes>`. **Result** - A list of mempool transactions in a :ref:`specified order `. Each mempool + A list of mempool transactions in a :ref:`canonical order `. Each mempool transaction is a dictionary with the following keys: * *height* @@ -1104,7 +1125,7 @@ Return an ordered list of UTXOs sent to a script hash. "nft": { "capability": "minting", "commitment": "f00fd00fb33f" - } + } }, "tx_hash": "87489c43bae69c297bbaf65276573b0001c20c647a3d54d2842a4425ff87bacc", "tx_pos": 1, @@ -1833,7 +1854,7 @@ Returns a dictionary containing various mempool stats obtained from the bitcoin **Signature** .. function:: mempool.get_info() - .. versionadded:: 1.6 + .. versionadded:: 1.6.0 **Result** From d94a696c0171fedb169858e8bac0423e06f03490 Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Fri, 24 Oct 2025 21:17:14 +0300 Subject: [PATCH 6/8] Added documentation for blockchain.transaction.broadcast_package Also cleaned up some other docs and small nits. --- protocol-changes.rst | 4 +- protocol-methods.rst | 127 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 114 insertions(+), 17 deletions(-) diff --git a/protocol-changes.rst b/protocol-changes.rst index a4d348a..123d111 100644 --- a/protocol-changes.rst +++ b/protocol-changes.rst @@ -283,7 +283,7 @@ Changes * The status of a scripthash has its definition tightened in a backwards-compatible way: mempool txs now have a :ref:`canonical ordering ` specified for the calculation (previously their order was unspecified). - * :func:`blockchain.estimatefee` changed to allow for an optional second argument, :const:`"mode"`. + * :func:`blockchain.estimatefee` changed to allow for an optional second argument, *mode*. * :func:`blockchain.scripthash.get_mempool`, :func:`blockchain.scripthash.get_history`, :func:`blockchain.address.get_mempool`, and :func:`blockchain.address.get_history` previously did not define an order for mempool transactions. We now mandate a :ref:`canonical ordering `. @@ -292,6 +292,8 @@ Changes New methods ----------- + * :func:`blockchain.transaction.broadcast_package` to broadcast a package of transactions via `submitpackage` + (BTC only). * :func:`mempool.get_info` to get more detailed and general relayfee info. Deprecated methods diff --git a/protocol-methods.rst b/protocol-methods.rst index b089c1d..80728e5 100644 --- a/protocol-methods.rst +++ b/protocol-methods.rst @@ -1227,6 +1227,101 @@ Protocol version 1.0 returning an error as the result: "258: txn-mempool-conflict" +blockchain.transaction.broadcast_package +======================================== + +*(BTC Only)* Broadcast a package of transactions to the network via the :const:`submitpackage` RPC. The package must +consist of a child with its parents, and none of the parents may depend on one another. The package must be +topologically sorted, with the child being the last element in the array. + +.. note:: Note that this RPC only available on BTC when the backing node is Bitcoin Core v28.0.0 or above. + +**Signature** + + .. function:: blockchain.transaction.broadcast_package(raw_txs, verbose=false) + + *raw_txs* + + An array of raw transactions, each as a hexadecimal string. + + *verbose* + + Whether a verbose coin-specific response is required. + +**Result** + + If *verbose* is :const:`false`: + + A dictionary with the following keys: + + * `success` + * Type: bool + * Value: Indicating the result of the package submission + * `errors` + * Type: Optional[List[Dict]] + * Value: Error message and txid (NOT wtxid) of transactions that were not accepted + + If *verbose* is :const:`true`: + + The bitcoind response according to its RPC API documentation. + Note that the exact structure and semantics can depend on the bitcoind version, + and hence the electrum protocol can make no guarantees about it. + +.. note:: The exact relay behaviour might depend on the bitcoind version of the server. + +.. note:: Server implementations should verify (e.g. by enforcing a minimum bitcoind version at runtime) + that the backing bitcoind supports relay of transaction packages. For example, note that Bitcoin Core 26.0 + already exposes the `submitpackage` RPC however it is effectively non-functional until Bitcoin Core 28.0. + +**Result Example** + +When *verbose* is :const:`false` (no errors):: + + { + "success": true + } + +When *verbose* is :const:`false` (with errors):: + + { + "success": false, + "errors": + [ + { + "txid": "ec6f295cd4b1b91f59cabb0ab8fdc7c76580db08be6426e465f75a69d82b9659", + "error": "bad-txns-inputs-missingorspent" + } + ] + } + +When *verbose* is :const:`true`:: + + { (json object) + "package_msg" : "str", (string) The transaction package result message. "success" indicates all transactions were accepted into or are already in the mempool. + "tx-results" : { (json object) transaction results keyed by wtxid + "wtxid" : { (json object) transaction wtxid + "txid" : "hex", (string) The transaction hash in hex + "other-wtxid" : "hex", (string, optional) The wtxid of a different transaction with the same txid but different witness found in the mempool. This means the submitted transaction was ignored. + "vsize" : n, (numeric, optional) Sigops-adjusted virtual transaction size. + "fees" : { (json object, optional) Transaction fees + "base" : n, (numeric) transaction fee in BTC + "effective-feerate" : n, (numeric, optional) if the transaction was not already in the mempool, the effective feerate in BTC per KvB. For example, the package feerate and/or feerate with modified fees from prioritisetransaction. + "effective-includes" : [ (json array, optional) if effective-feerate is provided, the wtxids of the transactions whose fees and vsizes are included in effective-feerate. + "hex", (string) transaction wtxid in hex + ... + ] + }, + "error" : "str" (string, optional) The transaction error string, if it was rejected by the mempool + }, + ... + }, + "replaced-transactions" : [ (json array, optional) List of txids of replaced transactions + "hex", (string) The transaction id + ... + ] + } + + blockchain.transaction.dsproof.get ================================== @@ -1849,7 +1944,7 @@ pool, weighted by transaction size. mempool.get_info ================ -Returns a dictionary containing various mempool stats obtained from the bitcoin daemon's :const:`"getmempoolinfo"` RPC. +Returns a dictionary containing various mempool stats obtained from the bitcoin daemon's `getmempoolinfo` RPC. **Signature** @@ -1862,7 +1957,7 @@ Returns a dictionary containing various mempool stats obtained from the bitcoin :const:`"mempoolminfee"`, :const:`"minrelaytxfee"`, :const:`"incrementalrelayfee"`, :const:`"unbroadcastcount"`, and :const:`"fullrbf"`. However, on BCH for example, some of the preceding keys may be be missing altogether since the bitcoin daemon does have the same BTC-specific mempool concepts related to fees and as a result does not provide - some of these keys via the :const:`"getmempoolinfo"` RPC. + some of these keys via the `getmempoolinfo` RPC. Of note is the following key: @@ -1872,22 +1967,22 @@ Returns a dictionary containing various mempool stats obtained from the bitcoin **Example Result** - :: +On BCH's Bitcoin Cash Node:: - // On BCH's Bitcoin Cash Node: - { - "mempoolminfee": 0.00001000, - "minrelaytxfee": 0.00001000 - } + { + "mempoolminfee": 0.00001000, + "minrelaytxfee": 0.00001000 + } - // On BTC's Bitcoin Core: - { - "mempoolminfee": 0.00001000, - "minrelaytxfee": 0.00001000, - "incrementalrelayfee": 0.00001000, - "unbroadcastcount": 5, - "fullrbf": true - } +On BTC's Bitcoin Core:: + + { + "mempoolminfee": 0.00001000, + "minrelaytxfee": 0.00001000, + "incrementalrelayfee": 0.00001000, + "unbroadcastcount": 5, + "fullrbf": true + } server.add_peer From 03e844709774430ad8ded1045e0753fc71fa782b Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Fri, 24 Oct 2025 21:41:20 +0300 Subject: [PATCH 7/8] Added broadcast_package (optional bool) to server.features map --- protocol-changes.rst | 2 ++ protocol-methods.rst | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/protocol-changes.rst b/protocol-changes.rst index 123d111..7b6362d 100644 --- a/protocol-changes.rst +++ b/protocol-changes.rst @@ -288,6 +288,8 @@ Changes :func:`blockchain.address.get_mempool`, and :func:`blockchain.address.get_history` previously did not define an order for mempool transactions. We now mandate a :ref:`canonical ordering `. * :func:`blockchain.block.headers` now returns headers as a list, instead of a single concatenated hex string + * :func:`server.features` now may return an additional optional boolean key, `broadcast_package` to indicate the + server provides :func:`blockchain.transaction.broadcast_package`. New methods ----------- diff --git a/protocol-methods.rst b/protocol-methods.rst index 80728e5..53a69d6 100644 --- a/protocol-methods.rst +++ b/protocol-methods.rst @@ -2065,6 +2065,8 @@ Return a list of features and services supported by the server. *cashtokens* key added (optional). .. versionchanged:: 1.5.3 *rpa* key added (optional). + .. versionchanged:: 1.6.0 + *broadcast_package* added (optional). **Result** @@ -2195,6 +2197,12 @@ Return a list of features and services supported by the server. The first block height which is indexed by this server for RPA-related RPC calls. Prefix searching of blocks before this height will always yield empty results. + * *broadcast_package* + + A boolean value. If present and set to :const:`true`, then the server definitely has + :func:`blockchain.transaction.broadcast_package` support. If this key is missing or :const:`false`, then the server + may not support this method. + **Example Result** :: From 797c322e22c3236b475005a073bf7c53dd10accf Mon Sep 17 00:00:00 2001 From: Calin Culianu Date: Wed, 5 Nov 2025 08:50:17 +0200 Subject: [PATCH 8/8] Added blockchain.transaction.broadcast_package error examples Ported from: https://github.com/spesmilo/electrum-protocol/pull/6 --- protocol-methods.rst | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/protocol-methods.rst b/protocol-methods.rst index 53a69d6..fc25c0b 100644 --- a/protocol-methods.rst +++ b/protocol-methods.rst @@ -1267,7 +1267,7 @@ topologically sorted, with the child being the last element in the array. Note that the exact structure and semantics can depend on the bitcoind version, and hence the electrum protocol can make no guarantees about it. -.. note:: The exact relay behaviour might depend on the bitcoind version of the server. +.. note:: The exact relay behavior might depend on the bitcoind version of the server. .. note:: Server implementations should verify (e.g. by enforcing a minimum bitcoind version at runtime) that the backing bitcoind supports relay of transaction packages. For example, note that Bitcoin Core 26.0 @@ -1294,7 +1294,7 @@ When *verbose* is :const:`false` (with errors):: ] } -When *verbose* is :const:`true`:: +When *verbose* is :const:`true` (comes from bitcoind; depends on bitcoind implementation and may change):: { (json object) "package_msg" : "str", (string) The transaction package result message. "success" indicates all transactions were accepted into or are already in the mempool. @@ -1321,6 +1321,27 @@ When *verbose* is :const:`true`:: ] } +.. note:: If the input is malformed, notably if :const:`raw_txs` is not a well-formed package, + errors will be sent back as JSON RPC error, as usual. Ultimately the distinction can depend on bitcoind. + Compare examples below where :const:`id=3` results in a JSON RPC error, but :const:`id=4` results in :const:`success=false` with an :const:`errors` array:: + + (request) {"id": 3, "method": "blockchain.transaction.broadcast_package", "params": [["02000000000102f758cda73a362840995d62d0079a22a11fc2652cb7740fffb8132486fe76fe730000000000fdffffff31b2d3d434d7c8f46b23a47aabc3c9498d4df5ffb9f13e13c3c3ecb52ab570b80000000000fdffffff02400d0300000000001600149f9aa3795e57c535d2f7b160ff023804c60fca6e30b3e80b00000000160014553a3b97c2cc7d3d4edeace3281ff038bd0676290247304402202c84cb82c94978b688154f06c3b91e64b99f2c2124d7c53004c81d23c4ce7e5102206a5c87986d1272140a8625a0fbd9926c2a93bf41517aca28615499140936109c0121027ba4d3ee6471a985307a37d09eb9a0a73c1a31b57616fe3e53a3d6d4540025190247304402203106d7622d3bcb415ae898558e8d414c337e2e7d4a485bbf4bffa8ba3bc620b802201340334f4d89e03735b3b183602ce4b3377225571ebbf18e97eaa24f3fcc5d4f0121027ba4d3ee6471a985307a37d09eb9a0a73c1a31b57616fe3e53a3d6d454002519a4090000", "02000000000101ab93b5c11c9a591b441de4228c942603a0479fa0d33a1a2bee72e75bd301c25e0000000000fdffffff01640c03000000000016001446116f48b60ad5b22377dcf951643e2e1aa3957a0247304402206002cc311d16b5cef7349bcd4174d3ae14d5741680ab81d7e24bc31c89634fd6022072d0d6b72c4ddbe62c527ebc5955677793cc13fe801ec22604d93bbc1b1f3291012102fb265cce2019e555fe23fb45e4cbc3d966bb399a52b2e93b808ad7961b426de1a4090000"]]} + (reply) { "jsonrpc": "2.0", + "error": { "code": 1, + "message": "the tx package was rejected by network rules.\n\npackage topology disallowed. not child-with-parents or parents depend on each other.." + }, + "id":3 + } + + (request) {"id": 4, "method": "blockchain.transaction.broadcast_package", "params": [["0200000000010148d372010d796a89f12864b5c86495d67bce98cb9ac3ba652bf811aa6b82c36c0000000000fdffffff01d20c030000000000160014e437538b7f13871562066babd1fbab72b4fba958024730440220662e4d917c0af3b37dd0bad65c9b46ba2a1504cd4f1346f35c0020f6d0edbd0e02207b44f98612bd3f58164385f4a572af2a2f471c924f6569aa6f3ddcaaa0e57753012103c3172a9f8820681c62b8bf28961988a4642b33f4920b9da14b06965c7fff83fd68090000", "02000000000101ab93b5c11c9a591b441de4228c942603a0479fa0d33a1a2bee72e75bd301c25e0000000000fdffffff014e0c03000000000016001446116f48b60ad5b22377dcf951643e2e1aa3957a0247304402202850349c76c41b801c429feaee96d773788b478c1cf051e967824fe504e5948902204bbede1252490c06c38feb206266aaf126937f6a3bc0a16d038fabde9781c6fd012102fb265cce2019e555fe23fb45e4cbc3d966bb399a52b2e93b808ad7961b426de1a4090000"]]} + (reply) { "jsonrpc": "2.0", + "result": { "success": false, + "errors": [{ "txid": "c13d8d63284853d3417a150cfddcfc62d31cab4b311c9322cb43f979a518ac3a", + "error": "insufficient fee, rejecting replacement c13d8d63284853d3417a150cfddcfc62d31cab4b311c9322cb43f979a518ac3a, not enough additional fees to relay; 0.00000022 < 0.0000011"}] + }, + "id":4 + } + blockchain.transaction.dsproof.get ==================================