aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcontrib/guix/guix-attest16
-rwxr-xr-xcontrib/guix/guix-verify6
-rw-r--r--contrib/seeds/nodes_main.txt14
-rw-r--r--doc/Doxyfile.in4
-rw-r--r--doc/files.md2
-rw-r--r--doc/i2p.md12
-rw-r--r--doc/release-notes.md17
-rw-r--r--doc/release-process.md23
-rw-r--r--src/addrdb.cpp13
-rw-r--r--src/addrdb.h6
-rw-r--r--src/addrman.cpp81
-rw-r--r--src/addrman.h62
-rw-r--r--src/banman.cpp2
-rw-r--r--src/banman.h2
-rw-r--r--src/bitcoin-cli.cpp35
-rw-r--r--src/chainparams.cpp7
-rw-r--r--src/chainparamsseeds.h12
-rw-r--r--src/consensus/params.h4
-rw-r--r--src/deploymentstatus.cpp17
-rw-r--r--src/init.cpp11
-rw-r--r--src/key.cpp2
-rw-r--r--src/net.h6
-rw-r--r--src/net_processing.cpp97
-rw-r--r--src/net_processing.h6
-rw-r--r--src/netaddress.h829
-rw-r--r--src/node/transaction.cpp2
-rw-r--r--src/outputtype.cpp17
-rw-r--r--src/outputtype.h3
-rw-r--r--src/pubkey.cpp2
-rw-r--r--src/qt/bitcoin.cpp53
-rw-r--r--src/qt/bitcoingui.cpp7
-rw-r--r--src/qt/createwalletdialog.cpp5
-rw-r--r--src/qt/createwalletdialog.h1
-rw-r--r--src/qt/peertablemodel.cpp10
-rw-r--r--src/qt/peertablemodel.h4
-rw-r--r--src/qt/peertablesortproxy.cpp2
-rw-r--r--src/qt/psbtoperationsdialog.cpp33
-rw-r--r--src/qt/test/apptests.cpp17
-rw-r--r--src/qt/transactionfilterproxy.cpp17
-rw-r--r--src/qt/transactionfilterproxy.h13
-rw-r--r--src/qt/transactionview.cpp14
-rw-r--r--src/qt/walletframe.cpp42
-rw-r--r--src/qt/walletframe.h1
-rw-r--r--src/qt/walletview.cpp42
-rw-r--r--src/qt/walletview.h2
-rw-r--r--src/rpc/blockchain.cpp3
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/misc.cpp8
-rw-r--r--src/rpc/net.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp2
-rw-r--r--src/rpc/rawtransaction_util.cpp9
-rw-r--r--src/rpc/rawtransaction_util.h3
-rw-r--r--src/script/sign.cpp13
-rw-r--r--src/script/sign.h3
-rw-r--r--src/secp256k1/.cirrus.yml202
-rw-r--r--src/secp256k1/.gitignore9
-rw-r--r--src/secp256k1/Makefile.am22
-rw-r--r--src/secp256k1/README.md4
-rw-r--r--src/secp256k1/build-aux/m4/bitcoin_secp.m416
-rwxr-xr-xsrc/secp256k1/ci/cirrus.sh33
-rw-r--r--src/secp256k1/ci/linux-debian.Dockerfile18
-rw-r--r--src/secp256k1/configure.ac120
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.c5
-rw-r--r--src/secp256k1/contrib/lax_der_parsing.h6
-rw-r--r--src/secp256k1/contrib/lax_der_privatekey_parsing.c3
-rw-r--r--src/secp256k1/contrib/lax_der_privatekey_parsing.h6
-rw-r--r--src/secp256k1/include/secp256k1.h60
-rw-r--r--src/secp256k1/include/secp256k1_extrakeys.h21
-rw-r--r--src/secp256k1/include/secp256k1_schnorrsig.h115
-rw-r--r--src/secp256k1/obj/.gitignore0
-rw-r--r--src/secp256k1/src/bench_ecdh.c4
-rw-r--r--src/secp256k1/src/bench_ecmult.c224
-rw-r--r--src/secp256k1/src/bench_internal.c4
-rw-r--r--src/secp256k1/src/bench_recover.c4
-rw-r--r--src/secp256k1/src/bench_schnorrsig.c19
-rw-r--r--src/secp256k1/src/bench_sign.c2
-rw-r--r--src/secp256k1/src/bench_verify.c2
-rw-r--r--src/secp256k1/src/ecdsa_impl.h2
-rw-r--r--src/secp256k1/src/ecmult.h1
-rw-r--r--src/secp256k1/src/ecmult_gen.h1
-rw-r--r--src/secp256k1/src/gen_context.c8
-rw-r--r--src/secp256k1/src/group_impl.h12
-rw-r--r--src/secp256k1/src/modules/ecdh/main_impl.h4
-rw-r--r--src/secp256k1/src/modules/extrakeys/main_impl.h30
-rw-r--r--src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h2
-rw-r--r--src/secp256k1/src/modules/extrakeys/tests_impl.h40
-rw-r--r--src/secp256k1/src/modules/recovery/main_impl.h2
-rw-r--r--src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h2
-rw-r--r--src/secp256k1/src/modules/schnorrsig/main_impl.h69
-rw-r--r--src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h26
-rw-r--r--src/secp256k1/src/modules/schnorrsig/tests_impl.h235
-rw-r--r--src/secp256k1/src/secp256k1.c49
-rw-r--r--src/secp256k1/src/testrand_impl.h2
-rw-r--r--src/secp256k1/src/tests.c182
-rw-r--r--src/secp256k1/src/tests_exhaustive.c9
-rw-r--r--src/secp256k1/src/valgrind_ctime_test.c12
-rw-r--r--src/test/addrman_tests.cpp3
-rw-r--r--src/test/denialofservice_tests.cpp8
-rw-r--r--src/test/fuzz/addrman.cpp228
-rw-r--r--src/test/fuzz/banman.cpp8
-rw-r--r--src/test/fuzz/deserialize.cpp279
-rw-r--r--src/test/fuzz/kitchen_sink.cpp8
-rw-r--r--src/test/fuzz/script_sign.cpp3
-rw-r--r--src/test/fuzz/string.cpp3
-rw-r--r--src/test/miner_tests.cpp62
-rw-r--r--src/test/net_tests.cpp1
-rw-r--r--src/test/util/setup_common.cpp4
-rw-r--r--src/test/util/wallet.cpp3
-rw-r--r--src/test/validation_block_tests.cpp18
-rw-r--r--src/txmempool.cpp116
-rw-r--r--src/txmempool.h42
-rw-r--r--src/util/string.h8
-rw-r--r--src/util/system.cpp4
-rw-r--r--src/util/translation.h6
-rw-r--r--src/validation.cpp13
-rw-r--r--src/validation.h6
-rw-r--r--src/wallet/interfaces.cpp3
-rw-r--r--src/wallet/rpcdump.cpp17
-rw-r--r--src/wallet/rpcwallet.cpp39
-rw-r--r--src/wallet/scriptpubkeyman.cpp37
-rw-r--r--src/wallet/scriptpubkeyman.h20
-rw-r--r--src/wallet/spend.cpp4
-rw-r--r--src/wallet/test/coinselector_tests.cpp3
-rw-r--r--src/wallet/test/spend_tests.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp4
-rw-r--r--src/wallet/wallet.cpp43
-rw-r--r--src/wallet/wallet.h8
-rwxr-xr-xtest/functional/feature_block.py45
-rwxr-xr-xtest/functional/feature_config_args.py5
-rwxr-xr-xtest/functional/feature_dbcrash.py3
-rwxr-xr-xtest/functional/feature_dersig.py7
-rwxr-xr-xtest/functional/feature_segwit.py31
-rwxr-xr-xtest/functional/interface_rest.py8
-rwxr-xr-xtest/functional/mempool_accept.py4
-rwxr-xr-xtest/functional/mempool_accept_wtxid.py11
-rwxr-xr-xtest/functional/mempool_package_limits.py475
-rwxr-xr-xtest/functional/mining_prioritisetransaction.py8
-rwxr-xr-xtest/functional/p2p_addr_relay.py82
-rwxr-xr-xtest/functional/p2p_dns_seeds.py129
-rwxr-xr-xtest/functional/p2p_invalid_messages.py7
-rwxr-xr-xtest/functional/p2p_segwit.py222
-rwxr-xr-xtest/functional/rpc_addresses_deprecation.py3
-rwxr-xr-xtest/functional/rpc_blockchain.py5
-rwxr-xr-xtest/functional/rpc_createmultisig.py5
-rwxr-xr-xtest/functional/rpc_decodescript.py13
-rwxr-xr-xtest/functional/rpc_packages.py59
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py3
-rw-r--r--test/functional/test_framework/address.py10
-rw-r--r--test/functional/test_framework/bdb.py8
-rw-r--r--test/functional/test_framework/blocktools.py16
-rwxr-xr-xtest/functional/test_framework/messages.py24
-rw-r--r--test/functional/test_framework/netutil.py3
-rwxr-xr-xtest/functional/test_framework/p2p.py1
-rwxr-xr-xtest/functional/test_framework/script_util.py7
-rw-r--r--test/functional/test_framework/util.py7
-rw-r--r--test/functional/test_framework/wallet.py80
-rwxr-xr-xtest/functional/test_framework/wallet_util.py3
-rwxr-xr-xtest/functional/test_runner.py2
-rwxr-xr-xtest/functional/wallet_listdescriptors.py28
159 files changed, 3799 insertions, 1722 deletions
diff --git a/contrib/guix/guix-attest b/contrib/guix/guix-attest
index dcf709b542..1503c330b2 100755
--- a/contrib/guix/guix-attest
+++ b/contrib/guix/guix-attest
@@ -159,20 +159,6 @@ Hint: You may wish to remove the existing attestations and their signatures by
EOF
}
-# Given a document with unix line endings (just <LF>) in stdin, make all lines
-# end in <CR><LF> and make sure there's no trailing <LF> at the end of the file.
-#
-# This is necessary as cleartext signatures are calculated on text after their
-# line endings are canonicalized.
-#
-# For more information:
-# 1. https://security.stackexchange.com/a/104261
-# 2. https://datatracker.ietf.org/doc/html/rfc4880#section-7.1
-#
-rfc4880_normalize_document() {
- sed 's/$/\r/' | head -c -2
-}
-
echo "Attesting to build outputs for version: '${VERSION}'"
echo ""
@@ -188,7 +174,6 @@ mkdir -p "$outsigdir"
cat "${noncodesigned_fragments[@]}" \
| sort -u \
| sort -k2 \
- | rfc4880_normalize_document \
> "$temp_noncodesigned"
if [ -e noncodesigned.SHA256SUMS ]; then
# The SHA256SUMS already exists, make sure it's exactly what we
@@ -216,7 +201,6 @@ mkdir -p "$outsigdir"
cat "${sha256sum_fragments[@]}" \
| sort -u \
| sort -k2 \
- | rfc4880_normalize_document \
> "$temp_all"
if [ -e all.SHA256SUMS ]; then
# The SHA256SUMS already exists, make sure it's exactly what we
diff --git a/contrib/guix/guix-verify b/contrib/guix/guix-verify
index e4863f115b..02ae022741 100755
--- a/contrib/guix/guix-verify
+++ b/contrib/guix/guix-verify
@@ -77,11 +77,13 @@ verify() {
echo ""
echo "Hint: Either the signature is invalid or the public key is missing"
echo ""
+ failure=1
elif ! diff --report-identical "$compare_manifest" "$current_manifest" 1>&2; then
echo "ERR: The SHA256SUMS attestation in these two directories differ:"
echo " '${compare_manifest}'"
echo " '${current_manifest}'"
echo ""
+ failure=1
else
echo "Verified: '${current_manifest}'"
echo ""
@@ -166,3 +168,7 @@ if (( ${#all_noncodesigned[@]} + ${#all_all[@]} == 0 )); then
echo ""
exit 1
fi
+
+if [ -n "$failure" ]; then
+ exit 1
+fi
diff --git a/contrib/seeds/nodes_main.txt b/contrib/seeds/nodes_main.txt
index f7bfb6eb0a..b9dfdb4b0a 100644
--- a/contrib/seeds/nodes_main.txt
+++ b/contrib/seeds/nodes_main.txt
@@ -677,12 +677,20 @@ tddeij4qigtjr6jfnrmq6btnirmq5msgwcsdpcdjr7atftm7cxlqztid.onion:8333
vi5bnbxkleeqi6hfccjochnn65lcxlfqs4uwgmhudph554zibiusqnad.onion:8333
xqt25cobm5zqucac3634zfght72he6u3eagfyej5ellbhcdgos7t2had.onion:8333
-# manually added 2021-05 for minimal i2p bootstrap support
-72l3ucjkuscrbiiepoehuwqgknyzgo7zuix5ty4puwrkyhtmnsga.b32.i2p:0
+# manually added 2021-08 for minimal i2p bootstrap support
+4hllr6w55mbtemb3ebvlzl4zj6qke4si7zcob5qdyg63mjgq624a.b32.i2p:0
+6s33jtpvwzkiej3nff5qm72slgqljxhxn62hdt6m7nvynqsxqdda.b32.i2p:0
+a5qsnv3maw77mlmmzlcglu6twje6ttctd3fhpbfwcbpmewx6fczq.b32.i2p:0
+bitcornrd36coazsbzsz4pdebyzvaplmsalq4kpoljmn6cg6x5zq.b32.i2p:0
c4gfnttsuwqomiygupdqqqyy5y5emnk5c73hrfvatri67prd7vyq.b32.i2p:0
+dhtq2p76tyhi442aidb3vd2bv7yxxjuddpb2jydnnrl2ons5bhha.b32.i2p:0
gehtac45oaghz54ypyopim64mql7oad2bqclla74l6tfeolzmodq.b32.i2p:0
h3r6bkn46qxftwja53pxiykntegfyfjqtnzbm6iv6r5mungmqgmq.b32.i2p:0
hnbbyjpxx54623l555sta7pocy3se4sdgmuebi5k6reesz5rjp6q.b32.i2p:0
-pjs7or2ctvteeo5tu4bwyrtydeuhqhvdprtujn4daxr75jpebjxa.b32.i2p:0
+i3hcdakiz2tyvggkwefvdjoi7444kgvd2mbdfizjvv43q7zukezq.b32.i2p:0
+jz3s4eurm5vzjresf4mwo7oni4bk36daolwxh4iqtewakylgkxmq.b32.i2p:0
+kokkmpquqlkptu5hkmzqlttsmtwxicldr4so7wqsufk6bwf32nma.b32.i2p:0
+kvrde7mcgjhz3xzeltwy4gs2rxdfbnbs2wc67mh2pt43wjmjnmbq.b32.i2p:0
+shh2ewyegnuwnmdse5kl5toybdvzkvk2yj4zcowz6iwhhh3ykdfa.b32.i2p:0
wwbw7nqr3ahkqv62cuqfwgtneekvvpnuc4i4f6yo7tpoqjswvcwa.b32.i2p:0
zsxwyo6qcn3chqzwxnseusqgsnuw3maqnztkiypyfxtya4snkoka.b32.i2p:0
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 21bf587eaf..d8fd46d1c7 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -863,9 +863,7 @@ RECURSIVE = YES
EXCLUDE = src/crc32c \
src/leveldb \
- src/json \
- src/test \
- src/qt/test
+ src/json
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
diff --git a/doc/files.md b/doc/files.md
index e670d77ae5..f88d3f91a1 100644
--- a/doc/files.md
+++ b/doc/files.md
@@ -56,7 +56,6 @@ Subdirectory | File(s) | Description
`indexes/coinstats/db/` | LevelDB database | Coinstats index; *optional*, used if `-coinstatsindex=1`
`wallets/` | | [Contains wallets](#multi-wallet-environment); can be specified by `-walletdir` option; if `wallets/` subdirectory does not exist, wallets reside in the [data directory](#data-directory-location)
`./` | `anchors.dat` | Anchor IP address database, created on shutdown and deleted at startup. Anchors are last known outgoing block-relay-only peers that are tried to re-connect to on startup
-`./` | `banlist.dat` | Stores the addresses/subnets of banned nodes (deprecated). `bitcoind` or `bitcoin-qt` no longer save the banlist to this file, but read it on startup if `banlist.json` is not present.
`./` | `banlist.json` | Stores the addresses/subnets of banned nodes.
`./` | `bitcoin.conf` | User-defined [configuration settings](bitcoin-conf.md) for `bitcoind` or `bitcoin-qt`. File is not written to by the software and must be created manually. Path can be specified by `-conf` option
`./` | `bitcoind.pid` | Stores the process ID (PID) of `bitcoind` or `bitcoin-qt` while running; created at start and deleted on shutdown; can be specified by `-pid` option
@@ -114,6 +113,7 @@ These subdirectories and files are no longer used by Bitcoin Core:
Path | Description | Repository notes
---------------|-------------|-----------------
+`banlist.dat` | Stores the addresses/subnets of banned nodes; superseded by `banlist.json` in 22.0 and completely ignored in 23.0 | [PR #20966](https://github.com/bitcoin/bitcoin/pull/20966), [PR #22570](https://github.com/bitcoin/bitcoin/pull/22570)
`blktree/` | Blockchain index; replaced by `blocks/index/` in [0.8.0](https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-0.8.0.md#improvements) | [PR #2231](https://github.com/bitcoin/bitcoin/pull/2231), [`8fdc94cc`](https://github.com/bitcoin/bitcoin/commit/8fdc94cc8f0341e96b1edb3a5b56811c0b20bd15)
`coins/` | Unspent transaction output database; replaced by `chainstate/` in 0.8.0 | [PR #2231](https://github.com/bitcoin/bitcoin/pull/2231), [`8fdc94cc`](https://github.com/bitcoin/bitcoin/commit/8fdc94cc8f0341e96b1edb3a5b56811c0b20bd15)
`blkindex.dat` | Blockchain index BDB database; replaced by {`chainstate/`, `blocks/index/`, `blocks/revNNNNN.dat`<sup>[\[2\]](#note2)</sup>} in 0.8.0 | [PR #1677](https://github.com/bitcoin/bitcoin/pull/1677)
diff --git a/doc/i2p.md b/doc/i2p.md
index 27ef4d9d9f..3a507a25ab 100644
--- a/doc/i2p.md
+++ b/doc/i2p.md
@@ -47,13 +47,21 @@ information in the debug log about your I2P configuration and connections. Run
`bitcoin-cli help logging` for more information.
It is possible to restrict outgoing connections in the usual way with
-`onlynet=i2p`. I2P support was added to Bitcoin Core in version 22.0 (mid 2021)
+`onlynet=i2p`. I2P support was added to Bitcoin Core in version 22.0 (mid-2021)
and there may be fewer I2P peers than Tor or IP ones. Therefore, using
`onlynet=i2p` alone (without other `onlynet=`) may make a node more susceptible
to [Sybil attacks](https://en.bitcoin.it/wiki/Weaknesses#Sybil_attack). Use
`bitcoin-cli -addrinfo` to see the number of I2P addresses known to your node.
-## I2P related information in Bitcoin Core
+Another consideration with `onlynet=i2p` is that the initial blocks download
+phase when syncing up a new node can be very slow. This phase can be sped up by
+using other networks, for instance `onlynet=onion`, at the same time.
+
+In general, a node can be run with both onion and I2P hidden services (or
+any/all of IPv4/IPv6/onion/I2P), which can provide a potential fallback if one
+of the networks has issues.
+
+## I2P-related information in Bitcoin Core
There are several ways to see your I2P address in Bitcoin Core:
- in the debug log (grep for `AddLocal`, the I2P address ends in `.b32.i2p`)
diff --git a/doc/release-notes.md b/doc/release-notes.md
index cf9edd9b08..01ef3610c9 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -57,6 +57,10 @@ Notable changes
P2P and network changes
-----------------------
+- A bitcoind node will no longer rumour addresses to inbound peers by default.
+ They will become eligible for address gossip after sending an ADDR, ADDRV2,
+ or GETADDR message. (#21528)
+
Updated RPCs
------------
@@ -66,6 +70,16 @@ New RPCs
Build System
------------
+Files
+-----
+
+* On startup, the list of banned hosts and networks (via `setban` RPC) in
+ `banlist.dat` is ignored and only `banlist.json` is considered. Bitcoin Core
+ version 22.x is the only version that can read `banlist.dat` and also write
+ it to `banlist.json`. If `banlist.json` already exists, version 22.x will not
+ try to translate the `banlist.dat` into json. After an upgrade, `listbanned`
+ can be used to double check the parsed entries. (#22570)
+
New settings
------------
@@ -94,6 +108,9 @@ RPC
Tests
-----
+- For the `regtest` network the BIP 66 (DERSIG) activation height was changed
+ from 1251 to 102. (#22632)
+
Credits
=======
diff --git a/doc/release-process.md b/doc/release-process.md
index c57fa5b23a..1b6472e812 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -199,26 +199,13 @@ popd
### After 3 or more people have guix-built and their results match:
-Combine `all.SHA256SUMS` and `all.SHA256SUMS.asc` into a clear-signed
-`SHA256SUMS.asc` message:
-
-```sh
-echo -e "-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\n$(cat all.SHA256SUMS)\n$(cat filename.txt.asc)" > SHA256SUMS.asc
-```
-
-Here's an equivalent, more readable command if you're confident that you won't
-mess up whitespaces when copy-pasting:
+Combine the `all.SHA256SUMS.asc` file from all signers into `SHA256SUMS.asc`:
```bash
-cat << EOF > SHA256SUMS.asc
------BEGIN PGP SIGNED MESSAGE-----
-Hash: SHA256
-
-$(cat all.SHA256SUMS)
-$(cat all.SHA256SUMS.asc)
-EOF
+cat "$VERSION"/*/all.SHA256SUMS.asc > SHA256SUMS.asc
```
+
- Upload to the bitcoincore.org server (`/var/www/bin/bitcoin-core-${VERSION}`):
1. The contents of `./bitcoin/guix-build-${VERSION}/output`, except for
`*-debug*` files.
@@ -230,7 +217,9 @@ EOF
as save storage space *do not upload these to the bitcoincore.org server,
nor put them in the torrent*.
- 2. The combined clear-signed message you just created `SHA256SUMS.asc`
+ 2. The `SHA256SUMS` file
+
+ 3. The `SHA256SUMS.asc` combined signature file you just created
- Create a torrent of the `/var/www/bin/bitcoin-core-${VERSION}` directory such
that at the top level there is only one file: the `bitcoin-core-${VERSION}`
diff --git a/src/addrdb.cpp b/src/addrdb.cpp
index b8fd019bab..c3e224ee83 100644
--- a/src/addrdb.cpp
+++ b/src/addrdb.cpp
@@ -197,17 +197,16 @@ bool CBanDB::Write(const banmap_t& banSet)
return false;
}
-bool CBanDB::Read(banmap_t& banSet, bool& dirty)
+bool CBanDB::Read(banmap_t& banSet)
{
- // If the JSON banlist does not exist, then try to read the non-upgraded banlist.dat.
+ if (fs::exists(m_banlist_dat)) {
+ LogPrintf("banlist.dat ignored because it can only be read by " PACKAGE_NAME " version 22.x. Remove %s to silence this warning.\n", m_banlist_dat);
+ }
+ // If the JSON banlist does not exist, then recreate it
if (!fs::exists(m_banlist_json)) {
- // If this succeeds then we need to flush to disk in order to create the JSON banlist.
- dirty = true;
- return DeserializeFileDB(m_banlist_dat, banSet, CLIENT_VERSION);
+ return false;
}
- dirty = false;
-
std::map<std::string, util::SettingsValue> settings;
std::vector<std::string> errors;
diff --git a/src/addrdb.h b/src/addrdb.h
index 399103c991..1e0ccb1f60 100644
--- a/src/addrdb.h
+++ b/src/addrdb.h
@@ -76,7 +76,7 @@ public:
static bool Read(CAddrMan& addr, CDataStream& ssPeers);
};
-/** Access to the banlist databases (banlist.json and banlist.dat) */
+/** Access to the banlist database (banlist.json) */
class CBanDB
{
private:
@@ -95,11 +95,9 @@ public:
* Read the banlist from disk.
* @param[out] banSet The loaded list. Set if `true` is returned, otherwise it is left
* in an undefined state.
- * @param[out] dirty Indicates whether the loaded list needs flushing to disk. Set if
- * `true` is returned, otherwise it is left in an undefined state.
* @return true on success
*/
- bool Read(banmap_t& banSet, bool& dirty);
+ bool Read(banmap_t& banSet);
};
/**
diff --git a/src/addrman.cpp b/src/addrman.cpp
index 8192b4eba6..96139182d3 100644
--- a/src/addrman.cpp
+++ b/src/addrman.cpp
@@ -77,38 +77,6 @@ double CAddrInfo::GetChance(int64_t nNow) const
return fChance;
}
-void CAddrMan::RemoveInvalid()
-{
- for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
- for (size_t i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
- const auto id = vvNew[bucket][i];
- if (id != -1 && !mapInfo[id].IsValid()) {
- ClearNew(bucket, i);
- }
- }
- }
-
- for (size_t bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; ++bucket) {
- for (size_t i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
- const auto id = vvTried[bucket][i];
- if (id == -1) {
- continue;
- }
- const auto& addr_info = mapInfo[id];
- if (addr_info.IsValid()) {
- continue;
- }
- vvTried[bucket][i] = -1;
- --nTried;
- SwapRandom(addr_info.nRandomPos, vRandom.size() - 1);
- vRandom.pop_back();
- mapAddr.erase(addr_info);
- mapInfo.erase(id);
- m_tried_collisions.erase(id);
- }
- }
-}
-
CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
{
AssertLockHeld(cs);
@@ -138,7 +106,7 @@ CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, in
return &mapInfo[nId];
}
-void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
+void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2) const
{
AssertLockHeld(cs);
@@ -150,11 +118,13 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
int nId1 = vRandom[nRndPos1];
int nId2 = vRandom[nRndPos2];
- assert(mapInfo.count(nId1) == 1);
- assert(mapInfo.count(nId2) == 1);
+ const auto it_1{mapInfo.find(nId1)};
+ const auto it_2{mapInfo.find(nId2)};
+ assert(it_1 != mapInfo.end());
+ assert(it_2 != mapInfo.end());
- mapInfo[nId1].nRandomPos = nRndPos2;
- mapInfo[nId2].nRandomPos = nRndPos1;
+ it_1->second.nRandomPos = nRndPos2;
+ it_2->second.nRandomPos = nRndPos1;
vRandom[nRndPos1] = nId2;
vRandom[nRndPos2] = nId1;
@@ -410,7 +380,7 @@ void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
}
}
-CAddrInfo CAddrMan::Select_(bool newOnly)
+CAddrInfo CAddrMan::Select_(bool newOnly) const
{
AssertLockHeld(cs);
@@ -433,8 +403,9 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
}
int nId = vvTried[nKBucket][nKBucketPos];
- assert(mapInfo.count(nId) == 1);
- CAddrInfo& info = mapInfo[nId];
+ const auto it_found{mapInfo.find(nId)};
+ assert(it_found != mapInfo.end());
+ const CAddrInfo& info{it_found->second};
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
@@ -450,8 +421,9 @@ CAddrInfo CAddrMan::Select_(bool newOnly)
nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
}
int nId = vvNew[nUBucket][nUBucketPos];
- assert(mapInfo.count(nId) == 1);
- CAddrInfo& info = mapInfo[nId];
+ const auto it_found{mapInfo.find(nId)};
+ assert(it_found != mapInfo.end());
+ const CAddrInfo& info{it_found->second};
if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
return info;
fChanceFactor *= 1.2;
@@ -503,15 +475,15 @@ int CAddrMan::Check_()
for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
- if (vvTried[n][i] != -1) {
- if (!setTried.count(vvTried[n][i]))
- return -11;
- if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n)
- return -17;
- if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
- return -18;
- setTried.erase(vvTried[n][i]);
- }
+ if (vvTried[n][i] != -1) {
+ if (!setTried.count(vvTried[n][i]))
+ return -11;
+ if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n)
+ return -17;
+ if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
+ return -18;
+ setTried.erase(vvTried[n][i]);
+ }
}
}
@@ -539,7 +511,7 @@ int CAddrMan::Check_()
}
#endif
-void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network)
+void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const
{
AssertLockHeld(cs);
@@ -559,9 +531,10 @@ void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size
int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
SwapRandom(n, nRndPos);
- assert(mapInfo.count(vRandom[n]) == 1);
+ const auto it{mapInfo.find(vRandom[n])};
+ assert(it != mapInfo.end());
- const CAddrInfo& ai = mapInfo[vRandom[n]];
+ const CAddrInfo& ai{it->second};
// Filter by network (optional)
if (network != std::nullopt && ai.GetNetClass() != network) continue;
diff --git a/src/addrman.h b/src/addrman.h
index 1fc64ac07f..1dd1932421 100644
--- a/src/addrman.h
+++ b/src/addrman.h
@@ -55,9 +55,10 @@ private:
bool fInTried{false};
//! position in vRandom
- int nRandomPos{-1};
+ mutable int nRandomPos{-1};
friend class CAddrMan;
+ friend class CAddrManDeterministic;
public:
@@ -104,19 +105,23 @@ public:
* * Make sure no (localized) attacker can fill the entire table with his nodes/addresses.
*
* To that end:
- * * Addresses are organized into buckets.
- * * Addresses that have not yet been tried go into 1024 "new" buckets.
- * * Based on the address range (/16 for IPv4) of the source of information, 64 buckets are selected at random.
+ * * Addresses are organized into buckets that can each store up to 64 entries.
+ * * Addresses to which our node has not successfully connected go into 1024 "new" buckets.
+ * * Based on the address range (/16 for IPv4) of the source of information, or if an asmap is provided,
+ * the AS it belongs to (for IPv4/IPv6), 64 buckets are selected at random.
* * The actual bucket is chosen from one of these, based on the range in which the address itself is located.
+ * * The position in the bucket is chosen based on the full address.
* * One single address can occur in up to 8 different buckets to increase selection chances for addresses that
* are seen frequently. The chance for increasing this multiplicity decreases exponentially.
- * * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen
- * ones) is removed from it first.
+ * * When adding a new address to an occupied position of a bucket, it will not replace the existing entry
+ * unless that address is also stored in another bucket or it doesn't meet one of several quality criteria
+ * (see IsTerrible for exact criteria).
* * Addresses of nodes that are known to be accessible go into 256 "tried" buckets.
* * Each address range selects at random 8 of these buckets.
* * The actual bucket is chosen from one of these, based on the full address.
- * * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently
- * tried ones) is evicted from it, back to the "new" buckets.
+ * * When adding a new good address to an occupied position of a bucket, a FEELER connection to the
+ * old address is attempted. The old entry is only replaced and moved back to the "new" buckets if this
+ * attempt was unsuccessful.
* * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not
* be observable by adversaries.
* * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive)
@@ -365,7 +370,8 @@ public:
s >> info;
int nKBucket = info.GetTriedBucket(nKey, m_asmap);
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
- if (vvTried[nKBucket][nKBucketPos] == -1) {
+ if (info.IsValid()
+ && vvTried[nKBucket][nKBucketPos] == -1) {
info.nRandomPos = vRandom.size();
info.fInTried = true;
vRandom.push_back(nIdCount);
@@ -419,6 +425,9 @@ public:
const int entry_index{bucket_entry.second};
CAddrInfo& info = mapInfo[entry_index];
+ // Don't store the entry in the new bucket if it's not a valid address for our addrman
+ if (!info.IsValid()) continue;
+
// The entry shouldn't appear in more than
// ADDRMAN_NEW_BUCKETS_PER_ADDRESS. If it has already, just skip
// this bucket_entry.
@@ -441,7 +450,7 @@ public:
}
}
- // Prune new entries with refcount 0 (as a result of collisions).
+ // Prune new entries with refcount 0 (as a result of collisions or invalid address).
int nLostUnk = 0;
for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
if (it->second.fInTried == false && it->second.nRefCount == 0) {
@@ -453,11 +462,9 @@ public:
}
}
if (nLost + nLostUnk > 0) {
- LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions\n", nLostUnk, nLost);
+ LogPrint(BCLog::ADDRMAN, "addrman lost %i new and %i tried addresses due to collisions or invalid addresses\n", nLostUnk, nLost);
}
- RemoveInvalid();
-
Check();
}
@@ -579,7 +586,7 @@ public:
/**
* Choose an address to connect to.
*/
- CAddrInfo Select(bool newOnly = false)
+ CAddrInfo Select(bool newOnly = false) const
EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
@@ -596,7 +603,7 @@ public:
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
* @param[in] network Select only addresses of this network (nullopt = all).
*/
- std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network)
+ std::vector<CAddress> GetAddr(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
LOCK(cs);
@@ -631,12 +638,12 @@ protected:
uint256 nKey;
//! Source of random numbers for randomization in inner loops
- FastRandomContext insecure_rand;
+ mutable FastRandomContext insecure_rand GUARDED_BY(cs);
-private:
//! A mutex to protect the inner data structures.
mutable Mutex cs;
+private:
//! Serialization versions.
enum Format : uint8_t {
V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
@@ -669,7 +676,9 @@ private:
std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
//! randomly-ordered vector of all nIds
- std::vector<int> vRandom GUARDED_BY(cs);
+ //! This is mutable because it is unobservable outside the class, so any
+ //! changes to it (even in const methods) are also unobservable.
+ mutable std::vector<int> vRandom GUARDED_BY(cs);
// number of "tried" entries
int nTried GUARDED_BY(cs);
@@ -692,12 +701,11 @@ private:
//! Find an entry.
CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
- //! find an entry, creating it if necessary.
- //! nTime and nServices of the found node are updated, if necessary.
+ //! Create a new entry and add it to the internal data structures mapInfo, mapAddr and vRandom.
CAddrInfo* Create(const CAddress &addr, const CNetAddr &addrSource, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Swap two elements in vRandom.
- void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2) const EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Move an entry from the "new" table(s) to the "tried" table
void MakeTried(CAddrInfo& info, int nId) EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -718,7 +726,7 @@ private:
void Attempt_(const CService &addr, bool fCountFailure, int64_t nTime) EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Select an address to connect to, if newOnly is set to true, only the new table is selected from.
- CAddrInfo Select_(bool newOnly) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ CAddrInfo Select_(bool newOnly) const EXCLUSIVE_LOCKS_REQUIRED(cs);
//! See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
void ResolveCollisions_() EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -727,7 +735,7 @@ private:
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
//! Consistency check
- void Check()
+ void Check() const
EXCLUSIVE_LOCKS_REQUIRED(cs)
{
#ifdef DEBUG_ADDRMAN
@@ -741,7 +749,7 @@ private:
#ifdef DEBUG_ADDRMAN
//! Perform consistency check. Returns an error code or zero.
- int Check_() EXCLUSIVE_LOCKS_REQUIRED(cs);
+ int Check_() const EXCLUSIVE_LOCKS_REQUIRED(cs);
#endif
/**
@@ -752,7 +760,7 @@ private:
* @param[in] max_pct Maximum percentage of addresses to return (0 = all).
* @param[in] network Select only addresses of this network (nullopt = all).
*/
- void GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const EXCLUSIVE_LOCKS_REQUIRED(cs);
/** We have successfully connected to this peer. Calling this function
* updates the CAddress's nTime, which is used in our IsTerrible()
@@ -770,10 +778,8 @@ private:
//! Update an entry's service bits.
void SetServices_(const CService &addr, ServiceFlags nServices) EXCLUSIVE_LOCKS_REQUIRED(cs);
- //! Remove invalid addresses.
- void RemoveInvalid() EXCLUSIVE_LOCKS_REQUIRED(cs);
-
friend class CAddrManTest;
+ friend class CAddrManDeterministic;
};
#endif // BITCOIN_ADDRMAN_H
diff --git a/src/banman.cpp b/src/banman.cpp
index d2437e6733..c64a48a05a 100644
--- a/src/banman.cpp
+++ b/src/banman.cpp
@@ -18,7 +18,7 @@ BanMan::BanMan(fs::path ban_file, CClientUIInterface* client_interface, int64_t
if (m_client_interface) m_client_interface->InitMessage(_("Loading banlist…").translated);
int64_t n_start = GetTimeMillis();
- if (m_ban_db.Read(m_banned, m_is_dirty)) {
+ if (m_ban_db.Read(m_banned)) {
SweepBanned(); // sweep out unused entries
LogPrint(BCLog::NET, "Loaded %d banned node addresses/subnets %dms\n", m_banned.size(),
diff --git a/src/banman.h b/src/banman.h
index 8c75d4037e..8a03a9e3fc 100644
--- a/src/banman.h
+++ b/src/banman.h
@@ -88,7 +88,7 @@ private:
RecursiveMutex m_cs_banned;
banmap_t m_banned GUARDED_BY(m_cs_banned);
- bool m_is_dirty GUARDED_BY(m_cs_banned);
+ bool m_is_dirty GUARDED_BY(m_cs_banned){false};
CClientUIInterface* m_client_interface = nullptr;
CBanDB m_ban_db;
const int64_t m_default_ban_time;
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 1ec6411e32..bc0af6398c 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -885,6 +885,29 @@ static void GetWalletBalances(UniValue& result)
}
/**
+ * GetProgressBar contructs a progress bar with 5% intervals.
+ *
+ * @param[in] progress The proportion of the progress bar to be filled between 0 and 1.
+ * @param[out] progress_bar String representation of the progress bar.
+ */
+static void GetProgressBar(double progress, std::string& progress_bar)
+{
+ if (progress < 0 || progress > 1) return;
+
+ static constexpr double INCREMENT{0.05};
+ static const std::string COMPLETE_BAR{"\u2592"};
+ static const std::string INCOMPLETE_BAR{"\u2591"};
+
+ for (int i = 0; i < progress / INCREMENT; ++i) {
+ progress_bar += COMPLETE_BAR;
+ }
+
+ for (int i = 0; i < (1 - progress) / INCREMENT; ++i) {
+ progress_bar += INCOMPLETE_BAR;
+ }
+}
+
+/**
* ParseGetInfoResult takes in -getinfo result in UniValue object and parses it
* into a user friendly UniValue string to be printed on the console.
* @param[out] result Reference to UniValue result containing the -getinfo output.
@@ -926,7 +949,17 @@ static void ParseGetInfoResult(UniValue& result)
std::string result_string = strprintf("%sChain: %s%s\n", BLUE, result["chain"].getValStr(), RESET);
result_string += strprintf("Blocks: %s\n", result["blocks"].getValStr());
result_string += strprintf("Headers: %s\n", result["headers"].getValStr());
- result_string += strprintf("Verification progress: %.4f%%\n", result["verificationprogress"].get_real() * 100);
+
+ const double ibd_progress{result["verificationprogress"].get_real()};
+ std::string ibd_progress_bar;
+ // Display the progress bar only if IBD progress is less than 99%
+ if (ibd_progress < 0.99) {
+ GetProgressBar(ibd_progress, ibd_progress_bar);
+ // Add padding between progress bar and IBD progress
+ ibd_progress_bar += " ";
+ }
+
+ result_string += strprintf("Verification progress: %s%.4f%%\n", ibd_progress_bar, ibd_progress * 100);
result_string += strprintf("Difficulty: %s\n\n", result["difficulty"].getValStr());
result_string += strprintf(
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index a8b45685d0..c3bbb147be 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -390,10 +390,10 @@ public:
consensus.signet_challenge.clear();
consensus.nSubsidyHalvingInterval = 150;
consensus.BIP16Exception = uint256();
- consensus.BIP34Height = 500; // BIP34 activated on regtest (Used in functional tests)
+ consensus.BIP34Height = 2; // BIP34 activated on regtest (Block at height 1 not enforced for testing purposes)
consensus.BIP34Hash = uint256();
consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in functional tests)
- consensus.BIP66Height = 1251; // BIP66 activated on regtest (Used in functional tests)
+ consensus.BIP66Height = 102; // BIP66 activated on regtest (Block at height 101 and earlier not enforced for testing purposes)
consensus.CSVHeight = 432; // CSV activated on regtest (Used in rpc activation tests)
consensus.SegwitHeight = 0; // SEGWIT is always activated on regtest unless overridden
consensus.MinBIP9WarningHeight = 0;
@@ -435,7 +435,8 @@ public:
assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"));
vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds.
- vSeeds.clear(); //!< Regtest mode doesn't have any DNS seeds.
+ vSeeds.clear();
+ vSeeds.emplace_back("dummySeed.invalid.");
fDefaultConsistencyChecks = true;
fRequireStandard = true;
diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h
index a22529c386..953a09d5e7 100644
--- a/src/chainparamsseeds.h
+++ b/src/chainparamsseeds.h
@@ -683,12 +683,20 @@ static const uint8_t chainparams_seed_main[] = {
0x04,0x20,0x98,0xc6,0x44,0x27,0x90,0x41,0xa6,0x98,0xf9,0x25,0x6c,0x59,0x0f,0x06,0x6d,0x44,0x59,0x0e,0xb2,0x46,0xb0,0xa4,0x37,0x88,0x69,0x8f,0xc1,0x32,0xcd,0x9f,0x15,0xd7,0x20,0x8d,
0x04,0x20,0xaa,0x3a,0x16,0x86,0xea,0x59,0x09,0x04,0x78,0xe5,0x10,0x92,0xe1,0x1d,0xad,0xf7,0x56,0x2b,0xac,0xb0,0x97,0x29,0x63,0x30,0xf4,0x1b,0xcf,0xde,0xf3,0x28,0x0a,0x29,0x20,0x8d,
0x04,0x20,0xbc,0x27,0xae,0x89,0xc1,0x67,0x73,0x0a,0x08,0x02,0xdf,0xb7,0xcc,0x94,0xc7,0x9f,0xf4,0x72,0x7a,0x9b,0x20,0x0c,0x5c,0x11,0x3d,0x22,0xd6,0x13,0x88,0x66,0x74,0xbf,0x20,0x8d,
- 0x05,0x20,0xfe,0x97,0xba,0x09,0x2a,0xa4,0x85,0x10,0xa1,0x04,0x7b,0x88,0x7a,0x5a,0x06,0x53,0x71,0x93,0x3b,0xf9,0xa2,0x2f,0xd9,0xe3,0x8f,0xa5,0xa2,0xac,0x1e,0x6c,0x6c,0x8c,0x00,0x00,
+ 0x05,0x20,0xe1,0xd6,0xb8,0xfa,0xdd,0xeb,0x03,0x32,0x30,0x3b,0x20,0x6a,0xbc,0xaf,0x99,0x4f,0xa0,0xa2,0x72,0x48,0xfe,0x44,0xe0,0xf6,0x03,0xc1,0xbd,0xb6,0x24,0xd0,0xf6,0xb8,0x00,0x00,
+ 0x05,0x20,0xf4,0xb7,0xb4,0xcd,0xf5,0xb6,0x54,0x82,0x27,0x6d,0x29,0x7b,0x06,0x7f,0x52,0x59,0xa0,0xb4,0xdc,0xf7,0x6f,0xb4,0x71,0xcf,0xcc,0xfb,0x6b,0x86,0xc2,0x57,0x80,0xc6,0x00,0x00,
+ 0x05,0x20,0x07,0x61,0x26,0xd7,0x6c,0x05,0xbf,0xf6,0x2d,0x8c,0xca,0xc4,0x65,0xd3,0xd3,0xb2,0x49,0xe9,0xcc,0x53,0x1e,0xca,0x77,0x84,0xb6,0x10,0x5e,0xc2,0x5a,0xfe,0x28,0xb3,0x00,0x00,
+ 0x05,0x20,0x0a,0x26,0x27,0x45,0xb1,0x1e,0xfc,0x27,0x03,0x32,0x0e,0x65,0x9e,0x3c,0x64,0x0e,0x33,0x50,0x3d,0x6c,0x90,0x17,0x0e,0x29,0xee,0x5a,0x58,0xdf,0x08,0xde,0xbf,0x73,0x00,0x00,
0x05,0x20,0x17,0x0c,0x56,0xce,0x72,0xa5,0xa0,0xe6,0x23,0x06,0xa3,0xc7,0x08,0x43,0x18,0xee,0x3a,0x46,0x35,0x5d,0x17,0xf6,0x78,0x96,0xa0,0x9c,0x51,0xef,0xbe,0x23,0xfd,0x71,0x00,0x00,
+ 0x05,0x20,0x19,0xe7,0x0d,0x3f,0xfe,0x9e,0x0e,0x8e,0x73,0x40,0x40,0xc3,0xba,0x8f,0x41,0xaf,0xf1,0x7b,0xa6,0x83,0x1b,0xc3,0xa4,0xe0,0x6d,0x6c,0x57,0xa7,0x36,0x5d,0x09,0xce,0x00,0x00,
0x05,0x20,0x31,0x0f,0x30,0x0b,0x9d,0x70,0x0c,0x7c,0xf7,0x98,0x7e,0x1c,0xf4,0x33,0xdc,0x64,0x17,0xf7,0x00,0x7a,0x0c,0x04,0xb5,0x83,0xfc,0x5f,0xa6,0x52,0x39,0x79,0x63,0x87,0x00,0x00,
0x05,0x20,0x3e,0xe3,0xe0,0xa9,0xbc,0xf4,0x2e,0x59,0xd9,0x20,0xee,0xdf,0x74,0x61,0x4d,0x99,0x0c,0x5c,0x15,0x30,0x9b,0x72,0x16,0x79,0x15,0xf4,0x7a,0xca,0x34,0xcc,0x81,0x99,0x00,0x00,
0x05,0x20,0x3b,0x42,0x1c,0x25,0xf7,0xbf,0x79,0xed,0x6d,0x7d,0xef,0x65,0x30,0x7d,0xee,0x16,0x37,0x22,0x72,0x43,0x33,0x28,0x40,0xa3,0xaa,0xf4,0x48,0x49,0x67,0xb1,0x4b,0xfd,0x00,0x00,
- 0x05,0x20,0x7a,0x65,0xf7,0x47,0x42,0x9d,0x66,0x42,0x3b,0xb3,0xa7,0x03,0x6c,0x46,0x78,0x19,0x28,0x78,0x1e,0xa3,0x7c,0x67,0x44,0xb7,0x83,0x05,0xe3,0xfe,0xa5,0xe4,0x0a,0x6e,0x00,0x00,
+ 0x05,0x20,0x46,0xce,0x21,0x81,0x48,0xce,0xa7,0x8a,0x98,0xca,0xb1,0x0b,0x51,0xa5,0xc8,0xff,0x39,0xc5,0x1a,0xa3,0xd3,0x02,0x32,0xa3,0x29,0xad,0x79,0xb8,0x7f,0x34,0x51,0x33,0x00,0x00,
+ 0x05,0x20,0x4e,0x77,0x2e,0x12,0x91,0x67,0x6b,0x94,0xc4,0x92,0x2f,0x19,0x67,0x7d,0xcd,0x47,0x02,0xad,0xf8,0x60,0x72,0xed,0x73,0xf1,0x10,0x99,0x2c,0x05,0x61,0x66,0x55,0xd9,0x00,0x00,
+ 0x05,0x20,0x53,0x94,0xa6,0x3e,0x14,0x82,0xd4,0xf9,0xd3,0xa7,0x53,0x33,0x05,0xce,0x72,0x64,0xed,0x74,0x09,0x63,0x8f,0x24,0xef,0xda,0x12,0xa1,0x55,0xe0,0xd8,0xbb,0xd3,0x58,0x00,0x00,
+ 0x05,0x20,0x55,0x62,0x32,0x7d,0x82,0x32,0x4f,0x9d,0xdf,0x24,0x5c,0xed,0x8e,0x1a,0x5a,0x8d,0xc6,0x50,0xb4,0x32,0xd5,0x85,0xef,0xb0,0xfa,0x7c,0xf9,0xbb,0x25,0x89,0x6b,0x03,0x00,0x00,
+ 0x05,0x20,0x91,0xcf,0xa2,0x5b,0x04,0x33,0x69,0x66,0xb0,0x72,0x27,0x54,0xbe,0xcd,0xd8,0x08,0xeb,0x95,0x55,0x5a,0xc2,0x79,0x91,0x3a,0xd9,0xf2,0x2c,0x73,0x9f,0x78,0x50,0xca,0x00,0x00,
0x05,0x20,0xb5,0x83,0x6f,0xb6,0x11,0xd8,0x0e,0xa8,0x57,0xda,0x15,0x20,0x5b,0x1a,0x6d,0x21,0x15,0x5a,0xbd,0xb4,0x17,0x11,0xc2,0xfb,0x0e,0xfc,0xde,0xe8,0x26,0x56,0xa8,0xac,0x00,0x00,
0x05,0x20,0xcc,0xaf,0x6c,0x3b,0xd0,0x13,0x76,0x23,0xc3,0x36,0xbb,0x64,0x4a,0x4a,0x06,0x93,0x69,0x6d,0xb0,0x10,0x6e,0x66,0xa4,0x61,0xf8,0x2d,0xe7,0x80,0x72,0x4d,0x53,0x94,0x00,0x00,
};
diff --git a/src/consensus/params.h b/src/consensus/params.h
index 9205cfee87..77bf7fd0d8 100644
--- a/src/consensus/params.h
+++ b/src/consensus/params.h
@@ -23,7 +23,7 @@ enum BuriedDeployment : int16_t {
DEPLOYMENT_CSV,
DEPLOYMENT_SEGWIT,
};
-constexpr bool ValidDeployment(BuriedDeployment dep) { return DEPLOYMENT_HEIGHTINCB <= dep && dep <= DEPLOYMENT_SEGWIT; }
+constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_SEGWIT; }
enum DeploymentPos : uint16_t {
DEPLOYMENT_TESTDUMMY,
@@ -31,7 +31,7 @@ enum DeploymentPos : uint16_t {
// NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp
MAX_VERSION_BITS_DEPLOYMENTS
};
-constexpr bool ValidDeployment(DeploymentPos dep) { return DEPLOYMENT_TESTDUMMY <= dep && dep <= DEPLOYMENT_TAPROOT; }
+constexpr bool ValidDeployment(DeploymentPos dep) { return dep < MAX_VERSION_BITS_DEPLOYMENTS; }
/**
* Struct for each individual consensus rule change using BIP9.
diff --git a/src/deploymentstatus.cpp b/src/deploymentstatus.cpp
index 9007800421..bba86639a3 100644
--- a/src/deploymentstatus.cpp
+++ b/src/deploymentstatus.cpp
@@ -7,6 +7,8 @@
#include <consensus/params.h>
#include <versionbits.h>
+#include <type_traits>
+
VersionBitsCache g_versionbitscache;
/* Basic sanity checking for BuriedDeployment/DeploymentPos enums and
@@ -15,3 +17,18 @@ VersionBitsCache g_versionbitscache;
static_assert(ValidDeployment(Consensus::DEPLOYMENT_TESTDUMMY), "sanity check of DeploymentPos failed (TESTDUMMY not valid)");
static_assert(!ValidDeployment(Consensus::MAX_VERSION_BITS_DEPLOYMENTS), "sanity check of DeploymentPos failed (MAX value considered valid)");
static_assert(!ValidDeployment(static_cast<Consensus::BuriedDeployment>(Consensus::DEPLOYMENT_TESTDUMMY)), "sanity check of BuriedDeployment failed (overlaps with DeploymentPos)");
+
+/* ValidDeployment only checks upper bounds for ensuring validity.
+ * This checks that the lowest possible value or the type is also a
+ * (specific) valid deployment so that lower bounds don't need to be checked.
+ */
+
+template<typename T, T x>
+static constexpr bool is_minimum()
+{
+ using U = typename std::underlying_type<T>::type;
+ return x == std::numeric_limits<U>::min();
+}
+
+static_assert(is_minimum<Consensus::BuriedDeployment, Consensus::DEPLOYMENT_HEIGHTINCB>(), "heightincb is not minimum value for BuriedDeployment");
+static_assert(is_minimum<Consensus::DeploymentPos, Consensus::DEPLOYMENT_TESTDUMMY>(), "testdummy is not minimum value for DeploymentPos");
diff --git a/src/init.cpp b/src/init.cpp
index 75394d96b1..1b406bed28 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -426,7 +426,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-dnsseed", strprintf("Query for peer addresses via DNS lookup, if low on addresses (default: %u unless -connect used)", DEFAULT_DNSSEED), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-externalip=<ip>", "Specify your own public address", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-fixedseeds", strprintf("Allow fixed seeds if DNS seeds don't provide peers (default: %u)", DEFAULT_FIXEDSEEDS), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
- argsman.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), ArgsManager::ALLOW_BOOL, OptionsCategory::CONNECTION);
argsman.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-listenonion", strprintf("Automatically create Tor onion service (default: %d)", DEFAULT_LISTEN_ONION), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u). This limit does not apply to connections manually added via -addnode or the addnode RPC, which have a separate limit of %u.", DEFAULT_MAX_PEER_CONNECTIONS, MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -848,6 +848,11 @@ bool AppInitParameterInteraction(const ArgsManager& args)
return InitError(_("Prune mode is incompatible with -coinstatsindex."));
}
+ // If -forcednsseed is set to true, ensure -dnsseed has not been set to false
+ if (args.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED) && !args.GetBoolArg("-dnsseed", DEFAULT_DNSSEED)){
+ return InitError(_("Cannot set -forcednsseed to true when setting -dnsseed to false."));
+ }
+
// -bind and -whitebind can't be set when not listening
size_t nUserBind = args.GetArgs("-bind").size() + args.GetArgs("-whitebind").size();
if (nUserBind != 0 && !args.GetBoolArg("-listen", DEFAULT_LISTEN)) {
@@ -1180,7 +1185,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
assert(!node.peerman);
node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(),
- *node.scheduler, chainman, *node.mempool, ignores_incoming_txs);
+ chainman, *node.mempool, ignores_incoming_txs);
RegisterValidationInterface(node.peerman.get());
// sanitize comments per BIP-0014, format user agent and check total size
@@ -1789,6 +1794,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
banman->DumpBanlist();
}, DUMP_BANS_INTERVAL);
+ if (node.peerman) node.peerman->StartScheduledTasks(*node.scheduler);
+
#if HAVE_SYSTEM
StartupNotify(args);
#endif
diff --git a/src/key.cpp b/src/key.cpp
index dcad386e77..7bef3d529b 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -274,7 +274,7 @@ bool CKey::SignSchnorr(const uint256& hash, Span<unsigned char> sig, const uint2
uint256 tweak = XOnlyPubKey(pubkey_bytes).ComputeTapTweakHash(merkle_root->IsNull() ? nullptr : merkle_root);
if (!secp256k1_keypair_xonly_tweak_add(GetVerifyContext(), &keypair, tweak.data())) return false;
}
- bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, secp256k1_nonce_function_bip340, aux ? (void*)aux->data() : nullptr);
+ bool ret = secp256k1_schnorrsig_sign(secp256k1_context_sign, sig.data(), hash.data(), &keypair, aux ? (unsigned char*)aux->data() : nullptr);
memory_cleanse(&keypair, sizeof(keypair));
return ret;
}
diff --git a/src/net.h b/src/net.h
index a8836dfcb4..889d57b74c 100644
--- a/src/net.h
+++ b/src/net.h
@@ -79,9 +79,9 @@ static const int64_t DEFAULT_PEER_CONNECT_TIMEOUT = 60;
/** Number of file descriptors required for message capture **/
static const int NUM_FDS_MESSAGE_CAPTURE = 1;
-static const bool DEFAULT_FORCEDNSSEED = false;
-static const bool DEFAULT_DNSSEED = true;
-static const bool DEFAULT_FIXEDSEEDS = true;
+static constexpr bool DEFAULT_FORCEDNSSEED{false};
+static constexpr bool DEFAULT_DNSSEED{true};
+static constexpr bool DEFAULT_FIXEDSEEDS{true};
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 2538904ade..8243ef0f55 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -225,9 +225,31 @@ struct Peer {
/** A vector of addresses to send to the peer, limited to MAX_ADDR_TO_SEND. */
std::vector<CAddress> m_addrs_to_send;
- /** Probabilistic filter of addresses that this peer already knows.
- * Used to avoid relaying addresses to this peer more than once. */
- const std::unique_ptr<CRollingBloomFilter> m_addr_known;
+ /** Probabilistic filter to track recent addr messages relayed with this
+ * peer. Used to avoid relaying redundant addresses to this peer.
+ *
+ * We initialize this filter for outbound peers (other than
+ * block-relay-only connections) or when an inbound peer sends us an
+ * address related message (ADDR, ADDRV2, GETADDR).
+ *
+ * Presence of this filter must correlate with m_addr_relay_enabled.
+ **/
+ std::unique_ptr<CRollingBloomFilter> m_addr_known;
+ /** Whether we are participating in address relay with this connection.
+ *
+ * We set this bool to true for outbound peers (other than
+ * block-relay-only connections), or when an inbound peer sends us an
+ * address related message (ADDR, ADDRV2, GETADDR).
+ *
+ * We use this bool to decide whether a peer is eligible for gossiping
+ * addr messages. This avoids relaying to peers that are unlikely to
+ * forward them, effectively blackholing self announcements. Reasons
+ * peers might support addr relay on the link include that they connected
+ * to us as a block-relay-only peer or they are a light client.
+ *
+ * This field must correlate with whether m_addr_known has been
+ * initialized.*/
+ std::atomic_bool m_addr_relay_enabled{false};
/** Whether a getaddr request to this peer is outstanding. */
bool m_getaddr_sent{false};
/** Guards address sending timers. */
@@ -259,9 +281,8 @@ struct Peer {
/** Work queue of items requested by this peer **/
std::deque<CInv> m_getdata_requests GUARDED_BY(m_getdata_requests_mutex);
- explicit Peer(NodeId id, bool addr_relay)
+ explicit Peer(NodeId id)
: m_id(id)
- , m_addr_known{addr_relay ? std::make_unique<CRollingBloomFilter>(5000, 0.001) : nullptr}
{}
};
@@ -271,7 +292,7 @@ class PeerManagerImpl final : public PeerManager
{
public:
PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, CAddrMan& addrman,
- BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman,
+ BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs);
/** Overridden from CValidationInterface. */
@@ -288,6 +309,7 @@ public:
bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing);
/** Implement PeerManager */
+ void StartScheduledTasks(CScheduler& scheduler) override;
void CheckForStaleTipAndEvictPeers() override;
bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const override;
bool IgnoresIncomingTxs() override { return m_ignore_incoming_txs; }
@@ -624,6 +646,14 @@ private:
* @param[in] vRecv The raw message received
*/
void ProcessGetCFCheckPt(CNode& peer, CDataStream& vRecv);
+
+ /** Checks if address relay is permitted with peer. If needed, initializes
+ * the m_addr_known bloom filter and sets m_addr_relay_enabled to true.
+ *
+ * @return True if address relay is enabled with peer
+ * False if address relay is disallowed
+ */
+ bool SetupAddressRelay(const CNode& node, Peer& peer);
};
} // namespace
@@ -744,11 +774,6 @@ static CNodeState *State(NodeId pnode) EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
return &it->second;
}
-static bool RelayAddrsWithPeer(const Peer& peer)
-{
- return peer.m_addr_known != nullptr;
-}
-
/**
* Whether the peer supports the address. For example, a peer that does not
* implement BIP155 cannot receive Tor v3 addresses because it requires
@@ -1129,9 +1154,7 @@ void PeerManagerImpl::InitializeNode(CNode *pnode)
assert(m_txrequest.Count(nodeid) == 0);
}
{
- // Addr relay is disabled for outbound block-relay-only peers to
- // prevent adversaries from inferring these links from addr traffic.
- PeerRef peer = std::make_shared<Peer>(nodeid, /* addr_relay = */ !pnode->IsBlockOnlyConn());
+ PeerRef peer = std::make_shared<Peer>(nodeid);
LOCK(m_peer_mutex);
m_peer_map.emplace_hint(m_peer_map.end(), nodeid, std::move(peer));
}
@@ -1270,6 +1293,7 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c
stats.m_ping_wait = ping_wait;
stats.m_addr_processed = peer->m_addr_processed.load();
stats.m_addr_rate_limited = peer->m_addr_rate_limited.load();
+ stats.m_addr_relay_enabled = peer->m_addr_relay_enabled.load();
return true;
}
@@ -1396,14 +1420,14 @@ bool PeerManagerImpl::BlockRequestAllowed(const CBlockIndex* pindex)
}
std::unique_ptr<PeerManager> PeerManager::make(const CChainParams& chainparams, CConnman& connman, CAddrMan& addrman,
- BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman,
+ BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs)
{
- return std::make_unique<PeerManagerImpl>(chainparams, connman, addrman, banman, scheduler, chainman, pool, ignore_incoming_txs);
+ return std::make_unique<PeerManagerImpl>(chainparams, connman, addrman, banman, chainman, pool, ignore_incoming_txs);
}
PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, CAddrMan& addrman,
- BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman,
+ BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs)
: m_chainparams(chainparams),
m_connman(connman),
@@ -1413,6 +1437,10 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn
m_mempool(pool),
m_ignore_incoming_txs(ignore_incoming_txs)
{
+}
+
+void PeerManagerImpl::StartScheduledTasks(CScheduler& scheduler)
+{
// Stale tip checking and peer eviction are on two different timers, but we
// don't want them to get out of sync due to drift in the scheduler, so we
// combine them in one function and schedule at the quicker (peer-eviction)
@@ -1684,7 +1712,7 @@ void PeerManagerImpl::RelayAddress(NodeId originator,
LOCK(m_peer_mutex);
for (auto& [id, peer] : m_peer_map) {
- if (RelayAddrsWithPeer(*peer) && id != originator && IsAddrCompatible(*peer, addr)) {
+ if (peer->m_addr_relay_enabled && id != originator && IsAddrCompatible(*peer, addr)) {
uint64_t hashKey = CSipHasher(hasher).Write(id).Finalize();
for (unsigned int i = 0; i < nRelayNodes; i++) {
if (hashKey > best[i].first) {
@@ -2574,7 +2602,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
UpdatePreferredDownload(pfrom, State(pfrom.GetId()));
}
- if (!pfrom.IsInboundConn() && !pfrom.IsBlockOnlyConn()) {
+ // Self advertisement & GETADDR logic
+ if (!pfrom.IsInboundConn() && SetupAddressRelay(pfrom, *peer)) {
// For outbound peers, we try to relay our address (so that other
// nodes can try to find us more quickly, as we have no guarantee
// that an outbound peer is even aware of how to reach us) and do a
@@ -2583,8 +2612,9 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// empty and no one will know who we are, so these mechanisms are
// important to help us connect to the network.
//
- // We skip this for block-relay-only peers to avoid potentially leaking
- // information about our block-relay-only connections via address relay.
+ // We skip this for block-relay-only peers. We want to avoid
+ // potentially leaking addr information and we do not want to
+ // indicate to the peer that we will participate in addr relay.
if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload())
{
CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
@@ -2782,10 +2812,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
s >> vAddr;
- if (!RelayAddrsWithPeer(*peer)) {
+ if (!SetupAddressRelay(pfrom, *peer)) {
LogPrint(BCLog::NET, "ignoring %s message from %s peer=%d\n", msg_type, pfrom.ConnectionTypeAsString(), pfrom.GetId());
return;
}
+
if (vAddr.size() > MAX_ADDR_TO_SEND)
{
Misbehaving(pfrom.GetId(), 20, strprintf("%s message size = %u", msg_type, vAddr.size()));
@@ -3718,6 +3749,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
return;
}
+ // Since this must be an inbound connection, SetupAddressRelay will
+ // never fail.
+ Assume(SetupAddressRelay(pfrom, *peer));
+
// Only send one GetAddr response per connection to reduce resource waste
// and discourage addr stamping of INV announcements.
if (peer->m_getaddr_recvd) {
@@ -4305,7 +4340,7 @@ void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::mic
void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::microseconds current_time)
{
// Nothing to do for non-address-relay peers
- if (!RelayAddrsWithPeer(peer)) return;
+ if (!peer.m_addr_relay_enabled) return;
LOCK(peer.m_addr_send_times_mutex);
// Periodically advertise our local address to the peer.
@@ -4433,6 +4468,22 @@ public:
};
}
+bool PeerManagerImpl::SetupAddressRelay(const CNode& node, Peer& peer)
+{
+ // We don't participate in addr relay with outbound block-relay-only
+ // connections to prevent providing adversaries with the additional
+ // information of addr traffic to infer the link.
+ if (node.IsBlockOnlyConn()) return false;
+
+ if (!peer.m_addr_relay_enabled.exchange(true)) {
+ // First addr message we have received from the peer, initialize
+ // m_addr_known
+ peer.m_addr_known = std::make_unique<CRollingBloomFilter>(5000, 0.001);
+ }
+
+ return true;
+}
+
bool PeerManagerImpl::SendMessages(CNode* pto)
{
PeerRef peer = GetPeerRef(pto->GetId());
diff --git a/src/net_processing.h b/src/net_processing.h
index c537efb5db..9d8d788583 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -31,16 +31,20 @@ struct CNodeStateStats {
std::vector<int> vHeightInFlight;
uint64_t m_addr_processed = 0;
uint64_t m_addr_rate_limited = 0;
+ bool m_addr_relay_enabled{false};
};
class PeerManager : public CValidationInterface, public NetEventsInterface
{
public:
static std::unique_ptr<PeerManager> make(const CChainParams& chainparams, CConnman& connman, CAddrMan& addrman,
- BanMan* banman, CScheduler& scheduler, ChainstateManager& chainman,
+ BanMan* banman, ChainstateManager& chainman,
CTxMemPool& pool, bool ignore_incoming_txs);
virtual ~PeerManager() { }
+ /** Begin running background tasks, should only be called once */
+ virtual void StartScheduledTasks(CScheduler& scheduler) = 0;
+
/** Get statistics from node state */
virtual bool GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) const = 0;
diff --git a/src/netaddress.h b/src/netaddress.h
index 5e1d9d2a6f..eb35ed3fac 100644
--- a/src/netaddress.h
+++ b/src/netaddress.h
@@ -42,8 +42,7 @@ static constexpr int ADDRV2_FORMAT = 0x20000000;
* over all enum values and also `GetExtNetwork()` "extends" this enum by
* introducing standalone constants starting from `NET_MAX`.
*/
-enum Network
-{
+enum Network {
/// Addresses from these networks are not publicly routable on the global Internet.
NET_UNROUTABLE = 0,
@@ -73,16 +72,14 @@ enum Network
/// Prefix of an IPv6 address when it contains an embedded IPv4 address.
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
static const std::array<uint8_t, 12> IPV4_IN_IPV6_PREFIX{
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF
-};
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF};
/// Prefix of an IPv6 address when it contains an embedded TORv2 address.
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
/// Such dummy IPv6 addresses are guaranteed to not be publicly routable as they
/// fall under RFC4193's fc00::/7 subnet allocated to unique-local addresses.
static const std::array<uint8_t, 6> TORV2_IN_IPV6_PREFIX{
- 0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43
-};
+ 0xFD, 0x87, 0xD8, 0x7E, 0xEB, 0x43};
/// Prefix of an IPv6 address when it contains an embedded "internal" address.
/// Used when (un)serializing addresses in ADDRv1 format (pre-BIP155).
@@ -120,354 +117,354 @@ static constexpr uint16_t I2P_SAM31_PORT{0};
*/
class CNetAddr
{
- protected:
- /**
- * Raw representation of the network address.
- * In network byte order (big endian) for IPv4 and IPv6.
- */
- prevector<ADDR_IPV6_SIZE, uint8_t> m_addr{ADDR_IPV6_SIZE, 0x0};
-
- /**
- * Network to which this address belongs.
- */
- Network m_net{NET_IPV6};
-
- /**
- * Scope id if scoped/link-local IPV6 address.
- * See https://tools.ietf.org/html/rfc4007
- */
- uint32_t m_scope_id{0};
-
- public:
- CNetAddr();
- explicit CNetAddr(const struct in_addr& ipv4Addr);
- void SetIP(const CNetAddr& ip);
-
- /**
- * Set from a legacy IPv6 address.
- * Legacy IPv6 address may be a normal IPv6 address, or another address
- * (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy
- * `addr` encoding.
- */
- void SetLegacyIPv6(Span<const uint8_t> ipv6);
-
- bool SetInternal(const std::string& name);
-
- /**
- * Parse a Tor or I2P address and set this object to it.
- * @param[in] addr Address to parse, for example
- * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
- * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
- * @returns Whether the operation was successful.
- * @see CNetAddr::IsTor(), CNetAddr::IsI2P()
- */
- bool SetSpecial(const std::string& addr);
-
- bool IsBindAny() const; // INADDR_ANY equivalent
- bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
- bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
- bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
- bool IsRFC2544() const; // IPv4 inter-network communications (198.18.0.0/15)
- bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10)
- bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24)
- bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
- bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
- bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16)
- bool IsRFC4193() const; // IPv6 unique local (FC00::/7)
- bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
- bool IsRFC4843() const; // IPv6 ORCHID (deprecated) (2001:10::/28)
- bool IsRFC7343() const; // IPv6 ORCHIDv2 (2001:20::/28)
- bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
- bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96)
- bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
- bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36)
- bool IsTor() const;
- bool IsI2P() const;
- bool IsCJDNS() const;
- bool IsLocal() const;
- bool IsRoutable() const;
- bool IsInternal() const;
- bool IsValid() const;
-
- /**
- * Check if the current object can be serialized in pre-ADDRv2/BIP155 format.
- */
- bool IsAddrV1Compatible() const;
-
- enum Network GetNetwork() const;
- std::string ToString() const;
- std::string ToStringIP() const;
- uint64_t GetHash() const;
- bool GetInAddr(struct in_addr* pipv4Addr) const;
- Network GetNetClass() const;
-
- //! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32.
- uint32_t GetLinkedIPv4() const;
- //! Whether this address has a linked IPv4 address (see GetLinkedIPv4()).
- bool HasLinkedIPv4() const;
-
- // The AS on the BGP path to the node we use to diversify
- // peers in AddrMan bucketing based on the AS infrastructure.
- // The ip->AS mapping depends on how asmap is constructed.
- uint32_t GetMappedAS(const std::vector<bool> &asmap) const;
-
- std::vector<unsigned char> GetGroup(const std::vector<bool> &asmap) const;
- std::vector<unsigned char> GetAddrBytes() const;
- int GetReachabilityFrom(const CNetAddr *paddrPartner = nullptr) const;
-
- explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
- bool GetIn6Addr(struct in6_addr* pipv6Addr) const;
-
- friend bool operator==(const CNetAddr& a, const CNetAddr& b);
- friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); }
- friend bool operator<(const CNetAddr& a, const CNetAddr& b);
-
- /**
- * Whether this address should be relayed to other peers even if we can't reach it ourselves.
- */
- bool IsRelayable() const
- {
- return IsIPv4() || IsIPv6() || IsTor() || IsI2P();
- }
+protected:
+ /**
+ * Raw representation of the network address.
+ * In network byte order (big endian) for IPv4 and IPv6.
+ */
+ prevector<ADDR_IPV6_SIZE, uint8_t> m_addr{ADDR_IPV6_SIZE, 0x0};
+
+ /**
+ * Network to which this address belongs.
+ */
+ Network m_net{NET_IPV6};
+
+ /**
+ * Scope id if scoped/link-local IPV6 address.
+ * See https://tools.ietf.org/html/rfc4007
+ */
+ uint32_t m_scope_id{0};
- /**
- * Serialize to a stream.
- */
- template <typename Stream>
- void Serialize(Stream& s) const
- {
- if (s.GetVersion() & ADDRV2_FORMAT) {
- SerializeV2Stream(s);
- } else {
- SerializeV1Stream(s);
- }
- }
+public:
+ CNetAddr();
+ explicit CNetAddr(const struct in_addr& ipv4Addr);
+ void SetIP(const CNetAddr& ip);
+
+ /**
+ * Set from a legacy IPv6 address.
+ * Legacy IPv6 address may be a normal IPv6 address, or another address
+ * (e.g. IPv4) disguised as IPv6. This encoding is used in the legacy
+ * `addr` encoding.
+ */
+ void SetLegacyIPv6(Span<const uint8_t> ipv6);
+
+ bool SetInternal(const std::string& name);
+
+ /**
+ * Parse a Tor or I2P address and set this object to it.
+ * @param[in] addr Address to parse, for example
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or
+ * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsTor(), CNetAddr::IsI2P()
+ */
+ bool SetSpecial(const std::string& addr);
+
+ bool IsBindAny() const; // INADDR_ANY equivalent
+ bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0)
+ bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor)
+ bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12)
+ bool IsRFC2544() const; // IPv4 inter-network communications (198.18.0.0/15)
+ bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10)
+ bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24)
+ bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32)
+ bool IsRFC3927() const; // IPv4 autoconfig (169.254.0.0/16)
+ bool IsRFC3964() const; // IPv6 6to4 tunnelling (2002::/16)
+ bool IsRFC4193() const; // IPv6 unique local (FC00::/7)
+ bool IsRFC4380() const; // IPv6 Teredo tunnelling (2001::/32)
+ bool IsRFC4843() const; // IPv6 ORCHID (deprecated) (2001:10::/28)
+ bool IsRFC7343() const; // IPv6 ORCHIDv2 (2001:20::/28)
+ bool IsRFC4862() const; // IPv6 autoconfig (FE80::/64)
+ bool IsRFC6052() const; // IPv6 well-known prefix for IPv4-embedded address (64:FF9B::/96)
+ bool IsRFC6145() const; // IPv6 IPv4-translated address (::FFFF:0:0:0/96) (actually defined in RFC2765)
+ bool IsHeNet() const; // IPv6 Hurricane Electric - https://he.net (2001:0470::/36)
+ bool IsTor() const;
+ bool IsI2P() const;
+ bool IsCJDNS() const;
+ bool IsLocal() const;
+ bool IsRoutable() const;
+ bool IsInternal() const;
+ bool IsValid() const;
+
+ /**
+ * Check if the current object can be serialized in pre-ADDRv2/BIP155 format.
+ */
+ bool IsAddrV1Compatible() const;
+
+ enum Network GetNetwork() const;
+ std::string ToString() const;
+ std::string ToStringIP() const;
+ uint64_t GetHash() const;
+ bool GetInAddr(struct in_addr* pipv4Addr) const;
+ Network GetNetClass() const;
+
+ //! For IPv4, mapped IPv4, SIIT translated IPv4, Teredo, 6to4 tunneled addresses, return the relevant IPv4 address as a uint32.
+ uint32_t GetLinkedIPv4() const;
+ //! Whether this address has a linked IPv4 address (see GetLinkedIPv4()).
+ bool HasLinkedIPv4() const;
+
+ // The AS on the BGP path to the node we use to diversify
+ // peers in AddrMan bucketing based on the AS infrastructure.
+ // The ip->AS mapping depends on how asmap is constructed.
+ uint32_t GetMappedAS(const std::vector<bool>& asmap) const;
+
+ std::vector<unsigned char> GetGroup(const std::vector<bool>& asmap) const;
+ std::vector<unsigned char> GetAddrBytes() const;
+ int GetReachabilityFrom(const CNetAddr* paddrPartner = nullptr) const;
+
+ explicit CNetAddr(const struct in6_addr& pipv6Addr, const uint32_t scope = 0);
+ bool GetIn6Addr(struct in6_addr* pipv6Addr) const;
+
+ friend bool operator==(const CNetAddr& a, const CNetAddr& b);
+ friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); }
+ friend bool operator<(const CNetAddr& a, const CNetAddr& b);
+
+ /**
+ * Whether this address should be relayed to other peers even if we can't reach it ourselves.
+ */
+ bool IsRelayable() const
+ {
+ return IsIPv4() || IsIPv6() || IsTor() || IsI2P();
+ }
- /**
- * Unserialize from a stream.
- */
- template <typename Stream>
- void Unserialize(Stream& s)
- {
- if (s.GetVersion() & ADDRV2_FORMAT) {
- UnserializeV2Stream(s);
- } else {
- UnserializeV1Stream(s);
- }
+ /**
+ * Serialize to a stream.
+ */
+ template <typename Stream>
+ void Serialize(Stream& s) const
+ {
+ if (s.GetVersion() & ADDRV2_FORMAT) {
+ SerializeV2Stream(s);
+ } else {
+ SerializeV1Stream(s);
}
+ }
- friend class CNetAddrHash;
- friend class CSubNet;
-
- private:
- /**
- * Parse a Tor address and set this object to it.
- * @param[in] addr Address to parse, must be a valid C string, for example
- * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.
- * @returns Whether the operation was successful.
- * @see CNetAddr::IsTor()
- */
- bool SetTor(const std::string& addr);
-
- /**
- * Parse an I2P address and set this object to it.
- * @param[in] addr Address to parse, must be a valid C string, for example
- * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
- * @returns Whether the operation was successful.
- * @see CNetAddr::IsI2P()
- */
- bool SetI2P(const std::string& addr);
-
- /**
- * BIP155 network ids recognized by this software.
- */
- enum BIP155Network : uint8_t {
- IPV4 = 1,
- IPV6 = 2,
- TORV2 = 3,
- TORV3 = 4,
- I2P = 5,
- CJDNS = 6,
- };
-
- /**
- * Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes).
- */
- static constexpr size_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE;
-
- /**
- * Maximum size of an address as defined in BIP155 (in bytes).
- * This is only the size of the address, not the entire CNetAddr object
- * when serialized.
- */
- static constexpr size_t MAX_ADDRV2_SIZE = 512;
-
- /**
- * Get the BIP155 network id of this address.
- * Must not be called for IsInternal() objects.
- * @returns BIP155 network id, except TORV2 which is no longer supported.
- */
- BIP155Network GetBIP155Network() const;
-
- /**
- * Set `m_net` from the provided BIP155 network id and size after validation.
- * @retval true the network was recognized, is valid and `m_net` was set
- * @retval false not recognised (from future?) and should be silently ignored
- * @throws std::ios_base::failure if the network is one of the BIP155 founding
- * networks (id 1..6) with wrong address size.
- */
- bool SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size);
-
- /**
- * Serialize in pre-ADDRv2/BIP155 format to an array.
- */
- void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const
- {
- size_t prefix_size;
-
- switch (m_net) {
- case NET_IPV6:
- assert(m_addr.size() == sizeof(arr));
- memcpy(arr, m_addr.data(), m_addr.size());
- return;
- case NET_IPV4:
- prefix_size = sizeof(IPV4_IN_IPV6_PREFIX);
- assert(prefix_size + m_addr.size() == sizeof(arr));
- memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size);
- memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
- return;
- case NET_INTERNAL:
- prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX);
- assert(prefix_size + m_addr.size() == sizeof(arr));
- memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
- memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
- return;
- case NET_ONION:
- case NET_I2P:
- case NET_CJDNS:
- break;
- case NET_UNROUTABLE:
- case NET_MAX:
- assert(false);
- } // no default case, so the compiler can warn about missing cases
-
- // Serialize ONION, I2P and CJDNS as all-zeros.
- memset(arr, 0x0, V1_SERIALIZATION_SIZE);
+ /**
+ * Unserialize from a stream.
+ */
+ template <typename Stream>
+ void Unserialize(Stream& s)
+ {
+ if (s.GetVersion() & ADDRV2_FORMAT) {
+ UnserializeV2Stream(s);
+ } else {
+ UnserializeV1Stream(s);
}
+ }
- /**
- * Serialize in pre-ADDRv2/BIP155 format to a stream.
- */
- template <typename Stream>
- void SerializeV1Stream(Stream& s) const
- {
- uint8_t serialized[V1_SERIALIZATION_SIZE];
+ friend class CNetAddrHash;
+ friend class CSubNet;
- SerializeV1Array(serialized);
+private:
+ /**
+ * Parse a Tor address and set this object to it.
+ * @param[in] addr Address to parse, must be a valid C string, for example
+ * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsTor()
+ */
+ bool SetTor(const std::string& addr);
+
+ /**
+ * Parse an I2P address and set this object to it.
+ * @param[in] addr Address to parse, must be a valid C string, for example
+ * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p.
+ * @returns Whether the operation was successful.
+ * @see CNetAddr::IsI2P()
+ */
+ bool SetI2P(const std::string& addr);
+
+ /**
+ * BIP155 network ids recognized by this software.
+ */
+ enum BIP155Network : uint8_t {
+ IPV4 = 1,
+ IPV6 = 2,
+ TORV2 = 3,
+ TORV3 = 4,
+ I2P = 5,
+ CJDNS = 6,
+ };
+
+ /**
+ * Size of CNetAddr when serialized as ADDRv1 (pre-BIP155) (in bytes).
+ */
+ static constexpr size_t V1_SERIALIZATION_SIZE = ADDR_IPV6_SIZE;
+
+ /**
+ * Maximum size of an address as defined in BIP155 (in bytes).
+ * This is only the size of the address, not the entire CNetAddr object
+ * when serialized.
+ */
+ static constexpr size_t MAX_ADDRV2_SIZE = 512;
+
+ /**
+ * Get the BIP155 network id of this address.
+ * Must not be called for IsInternal() objects.
+ * @returns BIP155 network id, except TORV2 which is no longer supported.
+ */
+ BIP155Network GetBIP155Network() const;
+
+ /**
+ * Set `m_net` from the provided BIP155 network id and size after validation.
+ * @retval true the network was recognized, is valid and `m_net` was set
+ * @retval false not recognised (from future?) and should be silently ignored
+ * @throws std::ios_base::failure if the network is one of the BIP155 founding
+ * networks (id 1..6) with wrong address size.
+ */
+ bool SetNetFromBIP155Network(uint8_t possible_bip155_net, size_t address_size);
+
+ /**
+ * Serialize in pre-ADDRv2/BIP155 format to an array.
+ */
+ void SerializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE]) const
+ {
+ size_t prefix_size;
+
+ switch (m_net) {
+ case NET_IPV6:
+ assert(m_addr.size() == sizeof(arr));
+ memcpy(arr, m_addr.data(), m_addr.size());
+ return;
+ case NET_IPV4:
+ prefix_size = sizeof(IPV4_IN_IPV6_PREFIX);
+ assert(prefix_size + m_addr.size() == sizeof(arr));
+ memcpy(arr, IPV4_IN_IPV6_PREFIX.data(), prefix_size);
+ memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
+ return;
+ case NET_INTERNAL:
+ prefix_size = sizeof(INTERNAL_IN_IPV6_PREFIX);
+ assert(prefix_size + m_addr.size() == sizeof(arr));
+ memcpy(arr, INTERNAL_IN_IPV6_PREFIX.data(), prefix_size);
+ memcpy(arr + prefix_size, m_addr.data(), m_addr.size());
+ return;
+ case NET_ONION:
+ case NET_I2P:
+ case NET_CJDNS:
+ break;
+ case NET_UNROUTABLE:
+ case NET_MAX:
+ assert(false);
+ } // no default case, so the compiler can warn about missing cases
+
+ // Serialize ONION, I2P and CJDNS as all-zeros.
+ memset(arr, 0x0, V1_SERIALIZATION_SIZE);
+ }
- s << serialized;
- }
+ /**
+ * Serialize in pre-ADDRv2/BIP155 format to a stream.
+ */
+ template <typename Stream>
+ void SerializeV1Stream(Stream& s) const
+ {
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
- /**
- * Serialize as ADDRv2 / BIP155.
- */
- template <typename Stream>
- void SerializeV2Stream(Stream& s) const
- {
- if (IsInternal()) {
- // Serialize NET_INTERNAL as embedded in IPv6. We need to
- // serialize such addresses from addrman.
- s << static_cast<uint8_t>(BIP155Network::IPV6);
- s << COMPACTSIZE(ADDR_IPV6_SIZE);
- SerializeV1Stream(s);
- return;
- }
+ SerializeV1Array(serialized);
- s << static_cast<uint8_t>(GetBIP155Network());
- s << m_addr;
- }
+ s << serialized;
+ }
- /**
- * Unserialize from a pre-ADDRv2/BIP155 format from an array.
- */
- void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE])
- {
- // Use SetLegacyIPv6() so that m_net is set correctly. For example
- // ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
- SetLegacyIPv6(arr);
+ /**
+ * Serialize as ADDRv2 / BIP155.
+ */
+ template <typename Stream>
+ void SerializeV2Stream(Stream& s) const
+ {
+ if (IsInternal()) {
+ // Serialize NET_INTERNAL as embedded in IPv6. We need to
+ // serialize such addresses from addrman.
+ s << static_cast<uint8_t>(BIP155Network::IPV6);
+ s << COMPACTSIZE(ADDR_IPV6_SIZE);
+ SerializeV1Stream(s);
+ return;
}
- /**
- * Unserialize from a pre-ADDRv2/BIP155 format from a stream.
- */
- template <typename Stream>
- void UnserializeV1Stream(Stream& s)
- {
- uint8_t serialized[V1_SERIALIZATION_SIZE];
+ s << static_cast<uint8_t>(GetBIP155Network());
+ s << m_addr;
+ }
+
+ /**
+ * Unserialize from a pre-ADDRv2/BIP155 format from an array.
+ */
+ void UnserializeV1Array(uint8_t (&arr)[V1_SERIALIZATION_SIZE])
+ {
+ // Use SetLegacyIPv6() so that m_net is set correctly. For example
+ // ::FFFF:0102:0304 should be set as m_net=NET_IPV4 (1.2.3.4).
+ SetLegacyIPv6(arr);
+ }
+
+ /**
+ * Unserialize from a pre-ADDRv2/BIP155 format from a stream.
+ */
+ template <typename Stream>
+ void UnserializeV1Stream(Stream& s)
+ {
+ uint8_t serialized[V1_SERIALIZATION_SIZE];
- s >> serialized;
+ s >> serialized;
+
+ UnserializeV1Array(serialized);
+ }
+
+ /**
+ * Unserialize from a ADDRv2 / BIP155 format.
+ */
+ template <typename Stream>
+ void UnserializeV2Stream(Stream& s)
+ {
+ uint8_t bip155_net;
+ s >> bip155_net;
- UnserializeV1Array(serialized);
+ size_t address_size;
+ s >> COMPACTSIZE(address_size);
+
+ if (address_size > MAX_ADDRV2_SIZE) {
+ throw std::ios_base::failure(strprintf(
+ "Address too long: %u > %u", address_size, MAX_ADDRV2_SIZE));
}
- /**
- * Unserialize from a ADDRv2 / BIP155 format.
- */
- template <typename Stream>
- void UnserializeV2Stream(Stream& s)
- {
- uint8_t bip155_net;
- s >> bip155_net;
-
- size_t address_size;
- s >> COMPACTSIZE(address_size);
-
- if (address_size > MAX_ADDRV2_SIZE) {
- throw std::ios_base::failure(strprintf(
- "Address too long: %u > %u", address_size, MAX_ADDRV2_SIZE));
+ m_scope_id = 0;
+
+ if (SetNetFromBIP155Network(bip155_net, address_size)) {
+ m_addr.resize(address_size);
+ s >> MakeSpan(m_addr);
+
+ if (m_net != NET_IPV6) {
+ return;
}
- m_scope_id = 0;
-
- if (SetNetFromBIP155Network(bip155_net, address_size)) {
- m_addr.resize(address_size);
- s >> MakeSpan(m_addr);
-
- if (m_net != NET_IPV6) {
- return;
- }
-
- // Do some special checks on IPv6 addresses.
-
- // Recognize NET_INTERNAL embedded in IPv6, such addresses are not
- // gossiped but could be coming from addrman, when unserializing from
- // disk.
- if (HasPrefix(m_addr, INTERNAL_IN_IPV6_PREFIX)) {
- m_net = NET_INTERNAL;
- memmove(m_addr.data(), m_addr.data() + INTERNAL_IN_IPV6_PREFIX.size(),
- ADDR_INTERNAL_SIZE);
- m_addr.resize(ADDR_INTERNAL_SIZE);
- return;
- }
-
- if (!HasPrefix(m_addr, IPV4_IN_IPV6_PREFIX) &&
- !HasPrefix(m_addr, TORV2_IN_IPV6_PREFIX)) {
- return;
- }
-
- // IPv4 and TORv2 are not supposed to be embedded in IPv6 (like in V1
- // encoding). Unserialize as !IsValid(), thus ignoring them.
- } else {
- // If we receive an unknown BIP155 network id (from the future?) then
- // ignore the address - unserialize as !IsValid().
- s.ignore(address_size);
+ // Do some special checks on IPv6 addresses.
+
+ // Recognize NET_INTERNAL embedded in IPv6, such addresses are not
+ // gossiped but could be coming from addrman, when unserializing from
+ // disk.
+ if (HasPrefix(m_addr, INTERNAL_IN_IPV6_PREFIX)) {
+ m_net = NET_INTERNAL;
+ memmove(m_addr.data(), m_addr.data() + INTERNAL_IN_IPV6_PREFIX.size(),
+ ADDR_INTERNAL_SIZE);
+ m_addr.resize(ADDR_INTERNAL_SIZE);
+ return;
}
- // Mimic a default-constructed CNetAddr object which is !IsValid() and thus
- // will not be gossiped, but continue reading next addresses from the stream.
- m_net = NET_IPV6;
- m_addr.assign(ADDR_IPV6_SIZE, 0x0);
+ if (!HasPrefix(m_addr, IPV4_IN_IPV6_PREFIX) &&
+ !HasPrefix(m_addr, TORV2_IN_IPV6_PREFIX)) {
+ return;
+ }
+
+ // IPv4 and TORv2 are not supposed to be embedded in IPv6 (like in V1
+ // encoding). Unserialize as !IsValid(), thus ignoring them.
+ } else {
+ // If we receive an unknown BIP155 network id (from the future?) then
+ // ignore the address - unserialize as !IsValid().
+ s.ignore(address_size);
}
+
+ // Mimic a default-constructed CNetAddr object which is !IsValid() and thus
+ // will not be gossiped, but continue reading next addresses from the stream.
+ m_net = NET_IPV6;
+ m_addr.assign(ADDR_IPV6_SIZE, 0x0);
+ }
};
class CNetAddrHash
@@ -488,104 +485,86 @@ private:
class CSubNet
{
- protected:
- /// Network (base) address
- CNetAddr network;
- /// Netmask, in network byte order
- uint8_t netmask[16];
- /// Is this value valid? (only used to signal parse errors)
- bool valid;
-
- bool SanityCheck() const;
-
- public:
- /**
- * Construct an invalid subnet (empty, `Match()` always returns false).
- */
- CSubNet();
-
- /**
- * Construct from a given network start and number of bits (CIDR mask).
- * @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is
- * created.
- * @param[in] mask CIDR mask, must be in [0, 32] for IPv4 addresses and in [0, 128] for
- * IPv6 addresses. Otherwise an invalid subnet is created.
- */
- CSubNet(const CNetAddr& addr, uint8_t mask);
-
- /**
- * Construct from a given network start and mask.
- * @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is
- * created.
- * @param[in] mask Network mask, must be of the same type as `addr` and not contain 0-bits
- * followed by 1-bits. Otherwise an invalid subnet is created.
- */
- CSubNet(const CNetAddr& addr, const CNetAddr& mask);
-
- /**
- * Construct a single-host subnet.
- * @param[in] addr The sole address to be contained in the subnet, can also be non-IPv[46].
- */
- explicit CSubNet(const CNetAddr& addr);
-
- bool Match(const CNetAddr &addr) const;
-
- std::string ToString() const;
- bool IsValid() const;
-
- friend bool operator==(const CSubNet& a, const CSubNet& b);
- friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); }
- friend bool operator<(const CSubNet& a, const CSubNet& b);
-
- SERIALIZE_METHODS(CSubNet, obj)
- {
- READWRITE(obj.network);
- if (obj.network.IsIPv4()) {
- // Before commit 102867c587f5f7954232fb8ed8e85cda78bb4d32, CSubNet used the last 4 bytes of netmask
- // to store the relevant bytes for an IPv4 mask. For compatibility reasons, keep doing so in
- // serialized form.
- unsigned char dummy[12] = {0};
- READWRITE(dummy);
- READWRITE(MakeSpan(obj.netmask).first(4));
- } else {
- READWRITE(obj.netmask);
- }
- READWRITE(obj.valid);
- // Mark invalid if the result doesn't pass sanity checking.
- SER_READ(obj, if (obj.valid) obj.valid = obj.SanityCheck());
- }
+protected:
+ /// Network (base) address
+ CNetAddr network;
+ /// Netmask, in network byte order
+ uint8_t netmask[16];
+ /// Is this value valid? (only used to signal parse errors)
+ bool valid;
+
+ bool SanityCheck() const;
+
+public:
+ /**
+ * Construct an invalid subnet (empty, `Match()` always returns false).
+ */
+ CSubNet();
+
+ /**
+ * Construct from a given network start and number of bits (CIDR mask).
+ * @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is
+ * created.
+ * @param[in] mask CIDR mask, must be in [0, 32] for IPv4 addresses and in [0, 128] for
+ * IPv6 addresses. Otherwise an invalid subnet is created.
+ */
+ CSubNet(const CNetAddr& addr, uint8_t mask);
+
+ /**
+ * Construct from a given network start and mask.
+ * @param[in] addr Network start. Must be IPv4 or IPv6, otherwise an invalid subnet is
+ * created.
+ * @param[in] mask Network mask, must be of the same type as `addr` and not contain 0-bits
+ * followed by 1-bits. Otherwise an invalid subnet is created.
+ */
+ CSubNet(const CNetAddr& addr, const CNetAddr& mask);
+
+ /**
+ * Construct a single-host subnet.
+ * @param[in] addr The sole address to be contained in the subnet, can also be non-IPv[46].
+ */
+ explicit CSubNet(const CNetAddr& addr);
+
+ bool Match(const CNetAddr& addr) const;
+
+ std::string ToString() const;
+ bool IsValid() const;
+
+ friend bool operator==(const CSubNet& a, const CSubNet& b);
+ friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); }
+ friend bool operator<(const CSubNet& a, const CSubNet& b);
};
/** A combination of a network address (CNetAddr) and a (TCP) port */
class CService : public CNetAddr
{
- protected:
- uint16_t port; // host order
-
- public:
- CService();
- CService(const CNetAddr& ip, uint16_t port);
- CService(const struct in_addr& ipv4Addr, uint16_t port);
- explicit CService(const struct sockaddr_in& addr);
- uint16_t GetPort() const;
- bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const;
- bool SetSockAddr(const struct sockaddr* paddr);
- friend bool operator==(const CService& a, const CService& b);
- friend bool operator!=(const CService& a, const CService& b) { return !(a == b); }
- friend bool operator<(const CService& a, const CService& b);
- std::vector<unsigned char> GetKey() const;
- std::string ToString() const;
- std::string ToStringPort() const;
- std::string ToStringIPPort() const;
-
- CService(const struct in6_addr& ipv6Addr, uint16_t port);
- explicit CService(const struct sockaddr_in6& addr);
-
- SERIALIZE_METHODS(CService, obj)
- {
- READWRITEAS(CNetAddr, obj);
- READWRITE(Using<BigEndianFormatter<2>>(obj.port));
- }
+protected:
+ uint16_t port; // host order
+
+public:
+ CService();
+ CService(const CNetAddr& ip, uint16_t port);
+ CService(const struct in_addr& ipv4Addr, uint16_t port);
+ explicit CService(const struct sockaddr_in& addr);
+ uint16_t GetPort() const;
+ bool GetSockAddr(struct sockaddr* paddr, socklen_t* addrlen) const;
+ bool SetSockAddr(const struct sockaddr* paddr);
+ friend bool operator==(const CService& a, const CService& b);
+ friend bool operator!=(const CService& a, const CService& b) { return !(a == b); }
+ friend bool operator<(const CService& a, const CService& b);
+ std::vector<unsigned char> GetKey() const;
+ std::string ToString() const;
+ std::string ToStringPort() const;
+ std::string ToStringIPPort() const;
+
+ CService(const struct in6_addr& ipv6Addr, uint16_t port);
+ explicit CService(const struct sockaddr_in6& addr);
+
+ SERIALIZE_METHODS(CService, obj)
+ {
+ READWRITEAS(CNetAddr, obj);
+ READWRITE(Using<BigEndianFormatter<2>>(obj.port));
+ }
};
bool SanityCheckASMap(const std::vector<bool>& asmap);
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 1861755aff..2a7bcc057f 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -125,8 +125,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
CTransactionRef GetTransaction(const CBlockIndex* const block_index, const CTxMemPool* const mempool, const uint256& hash, const Consensus::Params& consensusParams, uint256& hashBlock)
{
- LOCK(cs_main);
-
if (mempool && !block_index) {
CTransactionRef ptx = mempool->get(hash);
if (ptx) return ptx;
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index 8ede7b9974..b5f1df9792 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -13,6 +13,7 @@
#include <util/vector.h>
#include <assert.h>
+#include <optional>
#include <string>
static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
@@ -20,22 +21,18 @@ static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
static const std::string OUTPUT_TYPE_STRING_BECH32M = "bech32m";
-bool ParseOutputType(const std::string& type, OutputType& output_type)
+std::optional<OutputType> ParseOutputType(const std::string& type)
{
if (type == OUTPUT_TYPE_STRING_LEGACY) {
- output_type = OutputType::LEGACY;
- return true;
+ return OutputType::LEGACY;
} else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
- output_type = OutputType::P2SH_SEGWIT;
- return true;
+ return OutputType::P2SH_SEGWIT;
} else if (type == OUTPUT_TYPE_STRING_BECH32) {
- output_type = OutputType::BECH32;
- return true;
+ return OutputType::BECH32;
} else if (type == OUTPUT_TYPE_STRING_BECH32M) {
- output_type = OutputType::BECH32M;
- return true;
+ return OutputType::BECH32M;
}
- return false;
+ return std::nullopt;
}
const std::string& FormatOutputType(OutputType type)
diff --git a/src/outputtype.h b/src/outputtype.h
index 2b83235cd0..0de7689125 100644
--- a/src/outputtype.h
+++ b/src/outputtype.h
@@ -11,6 +11,7 @@
#include <script/standard.h>
#include <array>
+#include <optional>
#include <string>
#include <vector>
@@ -28,7 +29,7 @@ static constexpr auto OUTPUT_TYPES = std::array{
OutputType::BECH32M,
};
-[[nodiscard]] bool ParseOutputType(const std::string& str, OutputType& output_type);
+std::optional<OutputType> ParseOutputType(const std::string& str);
const std::string& FormatOutputType(OutputType type);
/**
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index 175a39b805..75202e7cf4 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -191,7 +191,7 @@ bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span<const unsigned char> si
assert(sigbytes.size() == 64);
secp256k1_xonly_pubkey pubkey;
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.data())) return false;
- return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey);
+ return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), 32, &pubkey);
}
static const CHashWriter HASHER_TAPTWEAK = TaggedHash("TapTweak");
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 4ab4e388d9..09f453363d 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -28,6 +28,7 @@
#include <qt/utilitydialog.h>
#include <qt/winshutdownmonitor.h>
#include <uint256.h>
+#include <util/string.h>
#include <util/system.h>
#include <util/threadnames.h>
#include <util/translation.h>
@@ -144,6 +145,53 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans
QApplication::installTranslator(&translator);
}
+static bool InitSettings()
+{
+ if (!gArgs.GetSettingsPath()) {
+ return true; // Do nothing if settings file disabled.
+ }
+
+ std::vector<std::string> errors;
+ if (!gArgs.ReadSettingsFile(&errors)) {
+ bilingual_str error = _("Settings file could not be read");
+ InitError(Untranslated(strprintf("%s:\n%s\n", error.original, MakeUnorderedList(errors))));
+
+ QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Reset | QMessageBox::Abort);
+ /*: Explanatory text shown on startup when the settings file cannot be read.
+ Prompts user to make a choice between resetting or aborting. */
+ messagebox.setInformativeText(QObject::tr("Do you want to reset settings to default values, or to abort without making changes?"));
+ messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(errors)));
+ messagebox.setTextFormat(Qt::PlainText);
+ messagebox.setDefaultButton(QMessageBox::Reset);
+ switch (messagebox.exec()) {
+ case QMessageBox::Reset:
+ break;
+ case QMessageBox::Abort:
+ return false;
+ default:
+ assert(false);
+ }
+ }
+
+ errors.clear();
+ if (!gArgs.WriteSettingsFile(&errors)) {
+ bilingual_str error = _("Settings file could not be written");
+ InitError(Untranslated(strprintf("%s:\n%s\n", error.original, MakeUnorderedList(errors))));
+
+ QMessageBox messagebox(QMessageBox::Critical, PACKAGE_NAME, QString::fromStdString(strprintf("%s.", error.translated)), QMessageBox::Ok);
+ /*: Explanatory text shown on startup when the settings file could not be written.
+ Prompts user to check that we have the ability to write to the file.
+ Explains that the user has the option of running without a settings file.*/
+ messagebox.setInformativeText(QObject::tr("A fatal error occurred. Check that settings file is writable, or try running with -nosettings."));
+ messagebox.setDetailedText(QString::fromStdString(MakeUnorderedList(errors)));
+ messagebox.setTextFormat(Qt::PlainText);
+ messagebox.setDefaultButton(QMessageBox::Ok);
+ messagebox.exec();
+ return false;
+ }
+ return true;
+}
+
/* qDebug() message handler --> debug.log */
void DebugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString &msg)
{
@@ -512,9 +560,8 @@ int GuiMain(int argc, char* argv[])
// Parse URIs on command line -- this can affect Params()
PaymentServer::ipcParseCommandLine(argc, argv);
#endif
- if (!gArgs.InitSettings(error)) {
- InitError(Untranslated(error));
- QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error initializing settings: %1").arg(QString::fromStdString(error)));
+
+ if (!InitSettings()) {
return EXIT_FAILURE;
}
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index f8aeb01659..fe606519af 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -110,6 +110,9 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
connect(activity, &CreateWalletActivity::finished, activity, &QObject::deleteLater);
activity->create();
});
+ connect(walletFrame, &WalletFrame::message, [this](const QString& title, const QString& message, unsigned int style) {
+ this->message(title, message, style);
+ });
setCentralWidget(walletFrame);
} else
#endif // ENABLE_WALLET
@@ -682,8 +685,6 @@ void BitcoinGUI::addWallet(WalletModel* walletModel)
m_wallet_selector_label_action->setVisible(true);
m_wallet_selector_action->setVisible(true);
}
- const QString display_name = walletModel->getDisplayName();
- m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
connect(wallet_view, &WalletView::outOfSyncWarningClicked, this, &BitcoinGUI::showModalOverlay);
connect(wallet_view, &WalletView::transactionClicked, this, &BitcoinGUI::gotoHistoryPage);
@@ -696,6 +697,8 @@ void BitcoinGUI::addWallet(WalletModel* walletModel)
connect(wallet_view, &WalletView::hdEnabledStatusChanged, this, &BitcoinGUI::updateWalletStatus);
connect(this, &BitcoinGUI::setPrivacy, wallet_view, &WalletView::setPrivacy);
wallet_view->setPrivacy(isPrivacyModeActivated());
+ const QString display_name = walletModel->getDisplayName();
+ m_wallet_selector->addItem(display_name, QVariant::fromValue(walletModel));
}
void BitcoinGUI::removeWallet(WalletModel* walletModel)
diff --git a/src/qt/createwalletdialog.cpp b/src/qt/createwalletdialog.cpp
index dc24bbc6a6..f9a61c3e60 100644
--- a/src/qt/createwalletdialog.cpp
+++ b/src/qt/createwalletdialog.cpp
@@ -32,7 +32,7 @@ CreateWalletDialog::CreateWalletDialog(QWidget* parent) :
// set to true, enable it when isEncryptWalletChecked is false.
ui->disable_privkeys_checkbox->setEnabled(!checked);
#ifdef ENABLE_EXTERNAL_SIGNER
- ui->external_signer_checkbox->setEnabled(!checked);
+ ui->external_signer_checkbox->setEnabled(m_has_signers && !checked);
#endif
// When the disable_privkeys_checkbox is disabled, uncheck it.
if (!ui->disable_privkeys_checkbox->isEnabled()) {
@@ -115,7 +115,8 @@ CreateWalletDialog::~CreateWalletDialog()
void CreateWalletDialog::setSigners(const std::vector<ExternalSigner>& signers)
{
- if (!signers.empty()) {
+ m_has_signers = !signers.empty();
+ if (m_has_signers) {
ui->external_signer_checkbox->setEnabled(true);
ui->external_signer_checkbox->setChecked(true);
ui->encrypt_wallet_checkbox->setEnabled(false);
diff --git a/src/qt/createwalletdialog.h b/src/qt/createwalletdialog.h
index 25ddf97585..fc13cc44eb 100644
--- a/src/qt/createwalletdialog.h
+++ b/src/qt/createwalletdialog.h
@@ -35,6 +35,7 @@ public:
private:
Ui::CreateWalletDialog *ui;
+ bool m_has_signers = false;
};
#endif // BITCOIN_QT_CREATEWALLETDIALOG_H
diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index 1b7fda6e77..98efaf29d7 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -72,8 +72,13 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
case NetNodeId:
return (qint64)rec->nodeStats.nodeid;
case Address:
- // prepend to peer address down-arrow symbol for inbound connection and up-arrow for outbound connection
- return QString::fromStdString((rec->nodeStats.fInbound ? "↓ " : "↑ ") + rec->nodeStats.addrName);
+ return QString::fromStdString(rec->nodeStats.addrName);
+ case Direction:
+ return QString(rec->nodeStats.fInbound ?
+ //: An Inbound Connection from a Peer.
+ tr("Inbound") :
+ //: An Outbound Connection to a Peer.
+ tr("Outbound"));
case ConnectionType:
return GUIUtil::ConnectionTypeToQString(rec->nodeStats.m_conn_type, /* prepend_direction */ false);
case Network:
@@ -94,6 +99,7 @@ QVariant PeerTableModel::data(const QModelIndex& index, int role) const
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case Address:
return {};
+ case Direction:
case ConnectionType:
case Network:
return QVariant(Qt::AlignCenter);
diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h
index 0d841ebf28..40265ee266 100644
--- a/src/qt/peertablemodel.h
+++ b/src/qt/peertablemodel.h
@@ -48,6 +48,7 @@ public:
enum ColumnIndex {
NetNodeId = 0,
Address,
+ Direction,
ConnectionType,
Network,
Ping,
@@ -84,6 +85,9 @@ private:
/*: Title of Peers Table column which contains the
IP/Onion/I2P address of the connected peer. */
tr("Address"),
+ /*: Title of Peers Table column which indicates the direction
+ the peer connection was initiated from. */
+ tr("Direction"),
/*: Title of Peers Table column which describes the type of
peer connection. The "type" describes why the connection exists. */
tr("Type"),
diff --git a/src/qt/peertablesortproxy.cpp b/src/qt/peertablesortproxy.cpp
index 78932da8d4..f92eef48f1 100644
--- a/src/qt/peertablesortproxy.cpp
+++ b/src/qt/peertablesortproxy.cpp
@@ -26,6 +26,8 @@ bool PeerTableSortProxy::lessThan(const QModelIndex& left_index, const QModelInd
return left_stats.nodeid < right_stats.nodeid;
case PeerTableModel::Address:
return left_stats.addrName.compare(right_stats.addrName) < 0;
+ case PeerTableModel::Direction:
+ return left_stats.fInbound > right_stats.fInbound; // default sort Inbound, then Outbound
case PeerTableModel::ConnectionType:
return left_stats.m_conn_type < right_stats.m_conn_type;
case PeerTableModel::Network:
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
index 2adfeeaaf0..289fb9f7c8 100644
--- a/src/qt/psbtoperationsdialog.cpp
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -47,18 +47,22 @@ void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
{
m_transaction_data = psbtx;
- bool complete;
- size_t n_could_sign;
- FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
- TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, &n_could_sign, m_transaction_data, complete);
- if (err != TransactionError::OK) {
- showStatus(tr("Failed to load transaction: %1")
- .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
- return;
+ bool complete = FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
+ if (m_wallet_model) {
+ size_t n_could_sign;
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, &n_could_sign, m_transaction_data, complete);
+ if (err != TransactionError::OK) {
+ showStatus(tr("Failed to load transaction: %1")
+ .arg(QString::fromStdString(TransactionErrorString(err).translated)),
+ StatusLevel::ERR);
+ return;
+ }
+ m_ui->signTransactionButton->setEnabled(!complete && !m_wallet_model->wallet().privateKeysDisabled() && n_could_sign > 0);
+ } else {
+ m_ui->signTransactionButton->setEnabled(false);
}
m_ui->broadcastTransactionButton->setEnabled(complete);
- m_ui->signTransactionButton->setEnabled(!complete && !m_wallet_model->wallet().privateKeysDisabled() && n_could_sign > 0);
updateTransactionDisplay();
}
@@ -133,7 +137,7 @@ void PSBTOperationsDialog::saveTransaction() {
}
CTxDestination address;
ExtractDestination(out.scriptPubKey, address);
- QString amount = BitcoinUnits::format(m_wallet_model->getOptionsModel()->getDisplayUnit(), out.nValue);
+ QString amount = BitcoinUnits::format(m_client_model->getOptionsModel()->getDisplayUnit(), out.nValue);
QString address_str = QString::fromStdString(EncodeDestination(address));
filename_suggestion.append(address_str + "-" + amount);
first = false;
@@ -224,6 +228,10 @@ void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
}
size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &psbtx) {
+ if (!m_wallet_model) {
+ return 0;
+ }
+
size_t n_signed;
bool complete;
TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, &n_signed, m_transaction_data, complete);
@@ -246,7 +254,10 @@ void PSBTOperationsDialog::showTransactionStatus(const PartiallySignedTransactio
case PSBTRole::SIGNER: {
QString need_sig_text = tr("Transaction still needs signature(s).");
StatusLevel level = StatusLevel::INFO;
- if (m_wallet_model->wallet().privateKeysDisabled()) {
+ if (!m_wallet_model) {
+ need_sig_text += " " + tr("(But no wallet is loaded.)");
+ level = StatusLevel::WARN;
+ } else if (m_wallet_model->wallet().privateKeysDisabled()) {
need_sig_text += " " + tr("(But this wallet cannot sign transactions.)");
level = StatusLevel::WARN;
} else if (n_could_sign < 1) {
diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp
index 9c31cd50df..8489b33144 100644
--- a/src/qt/test/apptests.cpp
+++ b/src/qt/test/apptests.cpp
@@ -12,7 +12,6 @@
#include <qt/rpcconsole.h>
#include <shutdown.h>
#include <test/util/setup_common.h>
-#include <univalue.h>
#include <validation.h>
#if defined(HAVE_CONFIG_H)
@@ -21,8 +20,10 @@
#include <QAction>
#include <QLineEdit>
+#include <QRegularExpression>
#include <QScopedPointer>
#include <QSignalSpy>
+#include <QString>
#include <QTest>
#include <QTextEdit>
#include <QtGlobal>
@@ -30,6 +31,13 @@
#include <QtTest/QtTestGui>
namespace {
+//! Regex find a string group inside of the console output
+QString FindInConsole(const QString& output, const QString& pattern)
+{
+ const QRegularExpression re(pattern);
+ return re.match(output).captured(1);
+}
+
//! Call getblockchaininfo RPC and check first field of JSON output.
void TestRpcCommand(RPCConsole* console)
{
@@ -41,10 +49,9 @@ void TestRpcCommand(RPCConsole* console)
QTest::keyClick(lineEdit, Qt::Key_Return);
QVERIFY(mw_spy.wait(1000));
QCOMPARE(mw_spy.count(), 4);
- QString output = messagesWidget->toPlainText();
- UniValue value;
- value.read(output.right(output.size() - output.lastIndexOf(QChar::ObjectReplacementCharacter) - 1).toStdString());
- QCOMPARE(value["chain"].get_str(), std::string("regtest"));
+ const QString output = messagesWidget->toPlainText();
+ const QString pattern = QStringLiteral("\"chain\": \"(\\w+)\"");
+ QCOMPARE(FindInConsole(output, pattern), QString("regtest"));
}
} // namespace
diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp
index a631f497af..75cbd6b3be 100644
--- a/src/qt/transactionfilterproxy.cpp
+++ b/src/qt/transactionfilterproxy.cpp
@@ -9,15 +9,8 @@
#include <cstdlib>
-// Earliest date that can be represented (far in the past)
-const QDateTime TransactionFilterProxy::MIN_DATE = QDateTime::fromTime_t(0);
-// Last date that can be represented (far in the future)
-const QDateTime TransactionFilterProxy::MAX_DATE = QDateTime::fromTime_t(0xFFFFFFFF);
-
TransactionFilterProxy::TransactionFilterProxy(QObject *parent) :
QSortFilterProxyModel(parent),
- dateFrom(MIN_DATE),
- dateTo(MAX_DATE),
m_search_string(),
typeFilter(ALL_TYPES),
watchOnlyFilter(WatchOnlyFilter_All),
@@ -46,8 +39,8 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &
return false;
QDateTime datetime = index.data(TransactionTableModel::DateRole).toDateTime();
- if (datetime < dateFrom || datetime > dateTo)
- return false;
+ if (dateFrom && datetime < *dateFrom) return false;
+ if (dateTo && datetime > *dateTo) return false;
QString address = index.data(TransactionTableModel::AddressRole).toString();
QString label = index.data(TransactionTableModel::LabelRole).toString();
@@ -65,10 +58,10 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &
return true;
}
-void TransactionFilterProxy::setDateRange(const QDateTime &from, const QDateTime &to)
+void TransactionFilterProxy::setDateRange(const std::optional<QDateTime>& from, const std::optional<QDateTime>& to)
{
- this->dateFrom = from;
- this->dateTo = to;
+ dateFrom = from;
+ dateTo = to;
invalidateFilter();
}
diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h
index 693b363692..09bc9e75db 100644
--- a/src/qt/transactionfilterproxy.h
+++ b/src/qt/transactionfilterproxy.h
@@ -10,6 +10,8 @@
#include <QDateTime>
#include <QSortFilterProxyModel>
+#include <optional>
+
/** Filter the transaction list according to pre-specified rules. */
class TransactionFilterProxy : public QSortFilterProxyModel
{
@@ -18,10 +20,6 @@ class TransactionFilterProxy : public QSortFilterProxyModel
public:
explicit TransactionFilterProxy(QObject *parent = nullptr);
- /** Earliest date that can be represented (far in the past) */
- static const QDateTime MIN_DATE;
- /** Last date that can be represented (far in the future) */
- static const QDateTime MAX_DATE;
/** Type filter bit field (all types) */
static const quint32 ALL_TYPES = 0xFFFFFFFF;
@@ -34,7 +32,8 @@ public:
WatchOnlyFilter_No
};
- void setDateRange(const QDateTime &from, const QDateTime &to);
+ /** Filter transactions between date range. Use std::nullopt for open range. */
+ void setDateRange(const std::optional<QDateTime>& from, const std::optional<QDateTime>& to);
void setSearchString(const QString &);
/**
@note Type filter takes a bit field created with TYPE() or ALL_TYPES
@@ -55,8 +54,8 @@ protected:
bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override;
private:
- QDateTime dateFrom;
- QDateTime dateTo;
+ std::optional<QDateTime> dateFrom;
+ std::optional<QDateTime> dateTo;
QString m_search_string;
quint32 typeFilter;
WatchOnlyFilter watchOnlyFilter;
diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp
index 83d17a32c0..908cb917f1 100644
--- a/src/qt/transactionview.cpp
+++ b/src/qt/transactionview.cpp
@@ -19,6 +19,8 @@
#include <node/ui_interface.h>
+#include <optional>
+
#include <QApplication>
#include <QComboBox>
#include <QDateTimeEdit>
@@ -266,26 +268,26 @@ void TransactionView::chooseDate(int idx)
{
case All:
transactionProxyModel->setDateRange(
- TransactionFilterProxy::MIN_DATE,
- TransactionFilterProxy::MAX_DATE);
+ std::nullopt,
+ std::nullopt);
break;
case Today:
transactionProxyModel->setDateRange(
GUIUtil::StartOfDay(current),
- TransactionFilterProxy::MAX_DATE);
+ std::nullopt);
break;
case ThisWeek: {
// Find last Monday
QDate startOfWeek = current.addDays(-(current.dayOfWeek()-1));
transactionProxyModel->setDateRange(
GUIUtil::StartOfDay(startOfWeek),
- TransactionFilterProxy::MAX_DATE);
+ std::nullopt);
} break;
case ThisMonth:
transactionProxyModel->setDateRange(
GUIUtil::StartOfDay(QDate(current.year(), current.month(), 1)),
- TransactionFilterProxy::MAX_DATE);
+ std::nullopt);
break;
case LastMonth:
transactionProxyModel->setDateRange(
@@ -295,7 +297,7 @@ void TransactionView::chooseDate(int idx)
case ThisYear:
transactionProxyModel->setDateRange(
GUIUtil::StartOfDay(QDate(current.year(), 1, 1)),
- TransactionFilterProxy::MAX_DATE);
+ std::nullopt);
break;
case Range:
dateRangeWidget->setVisible(true);
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index a1f357e0db..3d8bc0c7c5 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -4,12 +4,18 @@
#include <qt/walletframe.h>
+#include <node/ui_interface.h>
+#include <psbt.h>
+#include <qt/guiutil.h>
#include <qt/overviewpage.h>
+#include <qt/psbtoperationsdialog.h>
#include <qt/walletmodel.h>
#include <qt/walletview.h>
#include <cassert>
+#include <QApplication>
+#include <QClipboard>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
@@ -184,10 +190,40 @@ void WalletFrame::gotoVerifyMessageTab(QString addr)
void WalletFrame::gotoLoadPSBT(bool from_clipboard)
{
- WalletView *walletView = currentWalletView();
- if (walletView) {
- walletView->gotoLoadPSBT(from_clipboard);
+ std::string data;
+
+ if (from_clipboard) {
+ std::string raw = QApplication::clipboard()->text().toStdString();
+ bool invalid;
+ data = DecodeBase64(raw, &invalid);
+ if (invalid) {
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ } else {
+ QString filename = GUIUtil::getOpenFileName(this,
+ tr("Load Transaction Data"), QString(),
+ tr("Partially Signed Transaction (*.psbt)"), nullptr);
+ if (filename.isEmpty()) return;
+ if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
+ Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
+ data = std::string(std::istreambuf_iterator<char>{in}, {});
}
+
+ std::string error;
+ PartiallySignedTransaction psbtx;
+ if (!DecodeRawPSBT(psbtx, data, error)) {
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+
+ PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, currentWalletModel(), clientModel);
+ dlg->openWithPSBT(psbtx);
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ dlg->exec();
}
void WalletFrame::encryptWallet()
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index 4f77bd716f..fe42293abc 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -48,6 +48,7 @@ public:
Q_SIGNALS:
void createWalletButtonClicked();
+ void message(const QString& title, const QString& message, unsigned int style);
private:
QStackedWidget *walletStack;
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 3b8cf4c7ed..2326af80b6 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -8,7 +8,6 @@
#include <qt/askpassphrasedialog.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>
-#include <qt/psbtoperationsdialog.h>
#include <qt/optionsmodel.h>
#include <qt/overviewpage.h>
#include <qt/platformstyle.h>
@@ -21,13 +20,10 @@
#include <interfaces/node.h>
#include <node/ui_interface.h>
-#include <psbt.h>
#include <util/strencodings.h>
#include <QAction>
#include <QActionGroup>
-#include <QApplication>
-#include <QClipboard>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QProgressDialog>
@@ -205,44 +201,6 @@ void WalletView::gotoVerifyMessageTab(QString addr)
signVerifyMessageDialog->setAddress_VM(addr);
}
-void WalletView::gotoLoadPSBT(bool from_clipboard)
-{
- std::string data;
-
- if (from_clipboard) {
- std::string raw = QApplication::clipboard()->text().toStdString();
- bool invalid;
- data = DecodeBase64(raw, &invalid);
- if (invalid) {
- Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
- return;
- }
- } else {
- QString filename = GUIUtil::getOpenFileName(this,
- tr("Load Transaction Data"), QString(),
- tr("Partially Signed Transaction (*.psbt)"), nullptr);
- if (filename.isEmpty()) return;
- if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
- Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
- return;
- }
- std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
- data = std::string(std::istreambuf_iterator<char>{in}, {});
- }
-
- std::string error;
- PartiallySignedTransaction psbtx;
- if (!DecodeRawPSBT(psbtx, data, error)) {
- Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
- return;
- }
-
- PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, walletModel, clientModel);
- dlg->openWithPSBT(psbtx);
- dlg->setAttribute(Qt::WA_DeleteOnClose);
- dlg->exec();
-}
-
bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
{
return sendCoinsPage->handlePaymentRequest(recipient);
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index fedf06b710..5c42a9ffc0 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -83,8 +83,6 @@ public Q_SLOTS:
void gotoSignMessageTab(QString addr = "");
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
- /** Load Partially Signed Bitcoin Transaction */
- void gotoLoadPSBT(bool from_clipboard = false);
/** Show incoming transaction notification for new transactions.
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 4956ee39e9..909019d796 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -36,6 +36,7 @@
#include <txmempool.h>
#include <undo.h>
#include <util/strencodings.h>
+#include <util/string.h>
#include <util/system.h>
#include <util/translation.h>
#include <validation.h>
@@ -1328,7 +1329,7 @@ static RPCHelpMan verifychain()
"\nVerifies blockchain database.\n",
{
{"checklevel", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, range=0-4", DEFAULT_CHECKLEVEL)},
- strprintf("How thorough the block verification is:\n - %s", Join(CHECKLEVEL_DOC, "\n- "))},
+ strprintf("How thorough the block verification is:\n%s", MakeUnorderedList(CHECKLEVEL_DOC))},
{"nblocks", RPCArg::Type::NUM, RPCArg::DefaultHint{strprintf("%d, 0=all", DEFAULT_CHECKBLOCKS)}, "The number of blocks to check."},
},
RPCResult{
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 9c8582c7a3..9b5d181c4e 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -142,6 +142,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "importmulti", 0, "requests" },
{ "importmulti", 1, "options" },
{ "importdescriptors", 0, "requests" },
+ { "listdescriptors", 0, "private" },
{ "verifychain", 0, "checklevel" },
{ "verifychain", 1, "nblocks" },
{ "getblockstats", 0, "hash_or_height" },
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index 5178ce60e8..1a94abf6d3 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -24,6 +24,7 @@
#include <util/strencodings.h>
#include <util/system.h>
+#include <optional>
#include <stdint.h>
#include <tuple>
#ifdef HAVE_MALLOC_INFO
@@ -128,12 +129,13 @@ static RPCHelpMan createmultisig()
// Get the output type
OutputType output_type = OutputType::LEGACY;
if (!request.params[2].isNull()) {
- if (!ParseOutputType(request.params[2].get_str(), output_type)) {
+ std::optional<OutputType> parsed = ParseOutputType(request.params[2].get_str());
+ if (!parsed) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
- }
- if (output_type == OutputType::BECH32M) {
+ } else if (parsed.value() == OutputType::BECH32M) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "createmultisig cannot create bech32m multisig addresses");
}
+ output_type = parsed.value();
}
// Construct using pay-to-script-hash:
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index dba0f971b2..abc9ec3ce3 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -150,6 +150,7 @@ static RPCHelpMan getpeerinfo()
{
{RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"},
}},
+ {RPCResult::Type::BOOL, "addr_relay_enabled", "Whether we participate in address relay with this peer"},
{RPCResult::Type::ARR, "permissions", "Any special permissions that have been granted to this peer",
{
{RPCResult::Type::STR, "permission_type", Join(NET_PERMISSIONS_DOC, ",\n") + ".\n"},
@@ -242,6 +243,7 @@ static RPCHelpMan getpeerinfo()
heights.push_back(height);
}
obj.pushKV("inflight", heights);
+ obj.pushKV("addr_relay_enabled", statestats.m_addr_relay_enabled);
obj.pushKV("addr_processed", statestats.m_addr_processed);
obj.pushKV("addr_rate_limited", statestats.m_addr_rate_limited);
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index c617b0389c..00e77d89e5 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -903,7 +903,7 @@ static RPCHelpMan testmempoolaccept()
RPCResult{
RPCResult::Type::ARR, "", "The result of the mempool acceptance test for each raw transaction in the input array.\n"
"Returns results for each transaction in the same order they were passed in.\n"
- "It is possible for transactions to not be fully validated ('allowed' unset) if another transaction failed.\n",
+ "Transactions that cannot be fully validated due to failures in other transactions will not contain an 'allowed' result.\n",
{
{RPCResult::Type::OBJ, "", "",
{
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 122a92f084..f21eddf56c 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -18,6 +18,7 @@
#include <univalue.h>
#include <util/rbf.h>
#include <util/strencodings.h>
+#include <util/translation.h>
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, bool rbf)
{
@@ -280,22 +281,22 @@ void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
int nHashType = ParseSighashString(hashType);
// Script verification errors
- std::map<int, std::string> input_errors;
+ std::map<int, bilingual_str> input_errors;
bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors);
SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
}
-void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, std::string>& input_errors, UniValue& result)
+void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, bilingual_str>& input_errors, UniValue& result)
{
// Make errors UniValue
UniValue vErrors(UniValue::VARR);
for (const auto& err_pair : input_errors) {
- if (err_pair.second == "Missing amount") {
+ if (err_pair.second.original == "Missing amount") {
// This particular error needs to be an exception for some reason
throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coins.at(mtx.vin.at(err_pair.first).prevout).out.ToString()));
}
- TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second);
+ TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second.original);
}
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index ce7d5834fa..d2e116f7ee 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -8,6 +8,7 @@
#include <map>
#include <string>
+struct bilingual_str;
class FillableSigningProvider;
class UniValue;
struct CMutableTransaction;
@@ -25,7 +26,7 @@ class SigningProvider;
* @param result JSON object where signed transaction results accumulate
*/
void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result);
-void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, std::string>& input_errors, UniValue& result);
+void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, bilingual_str>& input_errors, UniValue& result);
/**
* Parse a prevtxs UniValue array and get the map of coins from it
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 7864e690d8..2faf7e5048 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -11,6 +11,7 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
+#include <util/translation.h>
#include <util/vector.h>
typedef std::vector<unsigned char> valtype;
@@ -629,7 +630,7 @@ bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
return false;
}
-bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, int nHashType, std::map<int, std::string>& input_errors)
+bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, int nHashType, std::map<int, bilingual_str>& input_errors)
{
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
@@ -661,7 +662,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
CTxIn& txin = mtx.vin[i];
auto coin = coins.find(txin.prevout);
if (coin == coins.end() || coin->second.IsSpent()) {
- input_errors[i] = "Input not found or already spent";
+ input_errors[i] = _("Input not found or already spent");
continue;
}
const CScript& prevPubKey = coin->second.out.scriptPubKey;
@@ -677,7 +678,7 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
// amount must be specified for valid segwit signature
if (amount == MAX_MONEY && !txin.scriptWitness.IsNull()) {
- input_errors[i] = "Missing amount";
+ input_errors[i] = _("Missing amount");
continue;
}
@@ -685,12 +686,12 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount, txdata, MissingDataBehavior::FAIL), &serror)) {
if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) {
// Unable to sign input and verification failed (possible attempt to partially sign).
- input_errors[i] = "Unable to sign input, invalid stack size (possibly missing key)";
+ input_errors[i] = Untranslated("Unable to sign input, invalid stack size (possibly missing key)");
} else if (serror == SCRIPT_ERR_SIG_NULLFAIL) {
// Verification failed (possibly due to insufficient signatures).
- input_errors[i] = "CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)";
+ input_errors[i] = Untranslated("CHECK(MULTI)SIG failing with non-zero signature (possibly need more signatures)");
} else {
- input_errors[i] = ScriptErrorString(serror);
+ input_errors[i] = Untranslated(ScriptErrorString(serror));
}
} else {
// If this input succeeds, make sure there is no error set for it
diff --git a/src/script/sign.h b/src/script/sign.h
index b4e7318892..b8fcac2e3c 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -21,6 +21,7 @@ class CScript;
class CTransaction;
class SigningProvider;
+struct bilingual_str;
struct CMutableTransaction;
/** Interface for signature creators. */
@@ -178,6 +179,6 @@ bool IsSolvable(const SigningProvider& provider, const CScript& script);
bool IsSegWitOutput(const SigningProvider& provider, const CScript& script);
/** Sign the CMutableTransaction */
-bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* provider, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors);
+bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* provider, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors);
#endif // BITCOIN_SCRIPT_SIGN_H
diff --git a/src/secp256k1/.cirrus.yml b/src/secp256k1/.cirrus.yml
index 506a860336..bf71a70839 100644
--- a/src/secp256k1/.cirrus.yml
+++ b/src/secp256k1/.cirrus.yml
@@ -1,21 +1,28 @@
env:
- WIDEMUL: auto
+ ### compiler options
+ HOST:
+ # Specific warnings can be disabled with -Wno-error=foo.
+ # -pedantic-errors is not equivalent to -Werror=pedantic and thus not implied by -Werror according to the GCC manual.
+ WERROR_CFLAGS: -Werror -pedantic-errors
+ MAKEFLAGS: -j2
+ BUILD: check
+ ### secp256k1 config
STATICPRECOMPUTATION: yes
ECMULTGENPRECISION: auto
ASM: no
- BUILD: check
+ WIDEMUL: auto
WITH_VALGRIND: yes
- RUN_VALGRIND: no
EXTRAFLAGS:
- HOST:
+ ### secp256k1 modules
+ EXPERIMENTAL: no
ECDH: no
RECOVERY: no
SCHNORRSIG: no
- EXPERIMENTAL: no
- CTIMETEST: yes
+ ### test options
+ TEST_ITERS:
BENCH: yes
- ITERS: 2
- MAKEFLAGS: -j2
+ BENCH_ITERS: 2
+ CTIMETEST: yes
cat_logs_snippet: &CAT_LOGS
always:
@@ -63,27 +70,8 @@ task:
- env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no}
- env: {CPPFLAGS: -DDETERMINISTIC}
- env: {CFLAGS: -O0, CTIMETEST: no}
- - env:
- CFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
- LDFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer"
- UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
- ASM: x86_64
- ECDH: yes
- RECOVERY: yes
- EXPERIMENTAL: yes
- SCHNORRSIG: yes
- CTIMETEST: no
- env: { ECMULTGENPRECISION: 2 }
- env: { ECMULTGENPRECISION: 8 }
- - env:
- RUN_VALGRIND: yes
- ASM: x86_64
- ECDH: yes
- RECOVERY: yes
- EXPERIMENTAL: yes
- SCHNORRSIG: yes
- EXTRAFLAGS: "--disable-openssl-tests"
- BUILD:
matrix:
- env:
CC: gcc
@@ -111,6 +99,7 @@ task:
CC: i686-linux-gnu-gcc
- env:
CC: clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include
+ << : *MERGE_BASE
test_script:
- ./ci/cirrus.sh
<< : *CAT_LOGS
@@ -181,9 +170,9 @@ task:
cpu: 1
memory: 1G
env:
- QEMU_CMD: qemu-s390x
+ WRAPPER_CMD: qemu-s390x
+ TEST_ITERS: 16
HOST: s390x-linux-gnu
- BUILD:
WITH_VALGRIND: no
ECDH: yes
RECOVERY: yes
@@ -196,3 +185,158 @@ task:
- rm /etc/ld.so.cache
- ./ci/cirrus.sh
<< : *CAT_LOGS
+
+task:
+ name: "ARM32: Linux (Debian stable, QEMU)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ WRAPPER_CMD: qemu-arm
+ TEST_ITERS: 16
+ HOST: arm-linux-gnueabihf
+ WITH_VALGRIND: no
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ CTIMETEST: no
+ matrix:
+ - env: {}
+ - env: {ASM: arm}
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "ARM64: Linux (Debian stable, QEMU)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ WRAPPER_CMD: qemu-aarch64
+ TEST_ITERS: 16
+ HOST: aarch64-linux-gnu
+ WITH_VALGRIND: no
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ CTIMETEST: no
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "ppc64le: Linux (Debian stable, QEMU)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ WRAPPER_CMD: qemu-ppc64le
+ TEST_ITERS: 16
+ HOST: powerpc64le-linux-gnu
+ WITH_VALGRIND: no
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ CTIMETEST: no
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ WRAPPER_CMD: wine64-stable
+ TEST_ITERS: 16
+ HOST: x86_64-w64-mingw32
+ WITH_VALGRIND: no
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ CTIMETEST: no
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+# Sanitizers
+task:
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ ECDH: yes
+ RECOVERY: yes
+ EXPERIMENTAL: yes
+ SCHNORRSIG: yes
+ CTIMETEST: no
+ EXTRAFLAGS: "--disable-openssl-tests"
+ matrix:
+ - name: "Valgrind (memcheck)"
+ env:
+ # The `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (https://www.valgrind.org/docs/manual/manual-core.html)
+ WRAPPER_CMD: "valgrind --error-exitcode=42"
+ TEST_ITERS: 16
+ - name: "UBSan, ASan, LSan"
+ env:
+ CFLAGS: "-fsanitize=undefined,address"
+ CFLAGS_FOR_BUILD: "-fsanitize=undefined,address"
+ UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
+ ASAN_OPTIONS: "strict_string_checks=1:detect_stack_use_after_return=1:detect_leaks=1"
+ LSAN_OPTIONS: "use_unaligned=1"
+ TEST_ITERS: 32
+ # Try to cover many configurations with just a tiny matrix.
+ matrix:
+ - env:
+ ASM: auto
+ STATICPRECOMPUTATION: yes
+ - env:
+ ASM: no
+ STATICPRECOMPUTATION: no
+ ECMULTGENPRECISION: 2
+ matrix:
+ - env:
+ CC: clang
+ - env:
+ HOST: i686-linux-gnu
+ CC: i686-linux-gnu-gcc
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
+
+task:
+ name: "C++ -fpermissive"
+ container:
+ dockerfile: ci/linux-debian.Dockerfile
+ cpu: 1
+ memory: 1G
+ env:
+ # ./configure correctly errors out when given CC=g++.
+ # We hack around this by passing CC=g++ only to make.
+ CC: gcc
+ MAKEFLAGS: -j2 CC=g++ CFLAGS=-fpermissive
+ WERROR_CFLAGS:
+ EXPERIMENTAL: yes
+ ECDH: yes
+ RECOVERY: yes
+ SCHNORRSIG: yes
+ << : *MERGE_BASE
+ test_script:
+ - ./ci/cirrus.sh
+ << : *CAT_LOGS
diff --git a/src/secp256k1/.gitignore b/src/secp256k1/.gitignore
index ccdef02b29..79b740db8a 100644
--- a/src/secp256k1/.gitignore
+++ b/src/secp256k1/.gitignore
@@ -23,6 +23,7 @@ aclocal.m4
autom4te.cache/
config.log
config.status
+conftest*
*.tar.gz
*.la
libtool
@@ -33,6 +34,14 @@ libtool
*~
*.log
*.trs
+
+coverage/
+coverage.html
+coverage.*.html
+*.gcda
+*.gcno
+*.gcov
+
src/libsecp256k1-config.h
src/libsecp256k1-config.h.in
src/ecmult_static_context.h
diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am
index 58c9635e53..1e03560884 100644
--- a/src/secp256k1/Makefile.am
+++ b/src/secp256k1/Makefile.am
@@ -1,5 +1,9 @@
ACLOCAL_AMFLAGS = -I build-aux/m4
+# AM_CFLAGS will be automatically prepended to CFLAGS by Automake when compiling some foo
+# which does not have an explicit foo_CFLAGS variable set.
+AM_CFLAGS = $(SECP_CFLAGS)
+
lib_LTLIBRARIES = libsecp256k1.la
include_HEADERS = include/secp256k1.h
include_HEADERS += include/secp256k1_preallocated.h
@@ -68,7 +72,7 @@ endif
endif
libsecp256k1_la_SOURCES = src/secp256k1.c
-libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
+libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB)
if VALGRIND_ENABLED
@@ -81,27 +85,27 @@ noinst_PROGRAMS += bench_verify bench_sign bench_internal bench_ecmult
bench_verify_SOURCES = src/bench_verify.c
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
# SECP_TEST_INCLUDES are only used here for CRYPTO_CPPFLAGS
-bench_verify_CPPFLAGS = -DSECP256K1_BUILD $(SECP_TEST_INCLUDES)
+bench_verify_CPPFLAGS = $(SECP_TEST_INCLUDES)
bench_sign_SOURCES = src/bench_sign.c
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
bench_internal_SOURCES = src/bench_internal.c
bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
-bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
+bench_internal_CPPFLAGS = $(SECP_INCLUDES)
bench_ecmult_SOURCES = src/bench_ecmult.c
bench_ecmult_LDADD = $(SECP_LIBS) $(COMMON_LIB)
-bench_ecmult_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
+bench_ecmult_CPPFLAGS = $(SECP_INCLUDES)
endif
TESTS =
if USE_TESTS
noinst_PROGRAMS += tests
tests_SOURCES = src/tests.c
-tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
+tests_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
if VALGRIND_ENABLED
tests_CPPFLAGS += -DVALGRIND
noinst_PROGRAMS += valgrind_ctime_test
valgrind_ctime_test_SOURCES = src/valgrind_ctime_test.c
-valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_LIBS) $(COMMON_LIB)
+valgrind_ctime_test_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
endif
if !ENABLE_COVERAGE
tests_CPPFLAGS += -DVERIFY
@@ -114,7 +118,7 @@ endif
if USE_EXHAUSTIVE_TESTS
noinst_PROGRAMS += exhaustive_tests
exhaustive_tests_SOURCES = src/tests_exhaustive.c
-exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDES)
+exhaustive_tests_CPPFLAGS = -I$(top_srcdir)/src $(SECP_INCLUDES)
if !ENABLE_COVERAGE
exhaustive_tests_CPPFLAGS += -DVERIFY
endif
@@ -129,10 +133,10 @@ CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) -I$(builddir)/src
gen_context_OBJECTS = gen_context.o
gen_context_BIN = gen_context$(BUILD_EXEEXT)
gen_%.o: src/gen_%.c src/libsecp256k1-config.h
- $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
+ $(CC_FOR_BUILD) $(DEFS) $(CPPFLAGS_FOR_BUILD) $(SECP_CFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@
$(gen_context_BIN): $(gen_context_OBJECTS)
- $(CC_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $^ -o $@
+ $(CC_FOR_BUILD) $(SECP_CFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) $^ -o $@
$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h
$(tests_OBJECTS): src/ecmult_static_context.h
diff --git a/src/secp256k1/README.md b/src/secp256k1/README.md
index 197a56fff8..182c29d9ce 100644
--- a/src/secp256k1/README.md
+++ b/src/secp256k1/README.md
@@ -17,6 +17,7 @@ Features:
* Suitable for embedded systems.
* Optional module for public key recovery.
* Optional module for ECDH key exchange.
+* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) (experimental).
Experimental features have not received enough scrutiny to satisfy the standard of quality of this library but are made available for testing and review by the community. The APIs of these features should not be considered stable.
@@ -96,7 +97,8 @@ To create a report, `gcovr` is recommended, as it includes branch coverage repor
To create a HTML report with coloured and annotated source code:
- $ gcovr --exclude 'src/bench*' --html --html-details -o coverage.html
+ $ mkdir -p coverage
+ $ gcovr --exclude 'src/bench*' --html --html-details -o coverage/coverage.html
Reporting a vulnerability
------------
diff --git a/src/secp256k1/build-aux/m4/bitcoin_secp.m4 b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
index e57888ca18..8245b2b863 100644
--- a/src/secp256k1/build-aux/m4/bitcoin_secp.m4
+++ b/src/secp256k1/build-aux/m4/bitcoin_secp.m4
@@ -82,3 +82,19 @@ if test x"$has_valgrind" != x"yes"; then
AC_CHECK_HEADER([valgrind/memcheck.h], [has_valgrind=yes; AC_DEFINE(HAVE_VALGRIND,1,[Define this symbol if valgrind is installed])])
fi
])
+
+dnl SECP_TRY_APPEND_CFLAGS(flags, VAR)
+dnl Append flags to VAR if CC accepts them.
+AC_DEFUN([SECP_TRY_APPEND_CFLAGS], [
+ AC_MSG_CHECKING([if ${CC} supports $1])
+ SECP_TRY_APPEND_CFLAGS_saved_CFLAGS="$CFLAGS"
+ CFLAGS="$1 $CFLAGS"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], [flag_works=yes], [flag_works=no])
+ AC_MSG_RESULT($flag_works)
+ CFLAGS="$SECP_TRY_APPEND_CFLAGS_saved_CFLAGS"
+ if test x"$flag_works" = x"yes"; then
+ $2="$$2 $1"
+ fi
+ unset flag_works
+ AC_SUBST($2)
+])
diff --git a/src/secp256k1/ci/cirrus.sh b/src/secp256k1/ci/cirrus.sh
index f26ca98d1d..27db1e6779 100755
--- a/src/secp256k1/ci/cirrus.sh
+++ b/src/secp256k1/ci/cirrus.sh
@@ -25,42 +25,27 @@ valgrind --version || true
make
# Print information about binaries so that we can see that the architecture is correct
-file *tests || true
+file *tests* || true
file bench_* || true
file .libs/* || true
-if [ -n "$BUILD" ]
-then
- make "$BUILD"
-fi
+# This tells `make check` to wrap test invocations.
+export LOG_COMPILER="$WRAPPER_CMD"
-if [ "$RUN_VALGRIND" = "yes" ]
-then
- # the `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (https://www.valgrind.org/docs/manual/manual-core.html)
- valgrind --error-exitcode=42 ./tests 16
- valgrind --error-exitcode=42 ./exhaustive_tests
-fi
+# This limits the iterations in the tests and benchmarks.
+export SECP256K1_TEST_ITERS="$TEST_ITERS"
+export SECP256K1_BENCH_ITERS="$BENCH_ITERS"
-if [ -n "$QEMU_CMD" ]
-then
- $QEMU_CMD ./tests 16
- $QEMU_CMD ./exhaustive_tests
-fi
+make "$BUILD"
if [ "$BENCH" = "yes" ]
then
# Using the local `libtool` because on macOS the system's libtool has nothing to do with GNU libtool
EXEC='./libtool --mode=execute'
- if [ -n "$QEMU_CMD" ]
- then
- EXEC="$EXEC $QEMU_CMD"
- fi
- if [ "$RUN_VALGRIND" = "yes" ]
+ if [ -n "$WRAPPER_CMD" ]
then
- EXEC="$EXEC valgrind --error-exitcode=42"
+ EXEC="$EXEC $WRAPPER_CMD"
fi
- # This limits the iterations in the benchmarks below to ITER iterations.
- export SECP256K1_BENCH_ITERS="$ITERS"
{
$EXEC ./bench_ecmult
$EXEC ./bench_internal
diff --git a/src/secp256k1/ci/linux-debian.Dockerfile b/src/secp256k1/ci/linux-debian.Dockerfile
index 5967cf8b31..2c02ed69d0 100644
--- a/src/secp256k1/ci/linux-debian.Dockerfile
+++ b/src/secp256k1/ci/linux-debian.Dockerfile
@@ -2,12 +2,24 @@ FROM debian:stable
RUN dpkg --add-architecture i386
RUN dpkg --add-architecture s390x
+RUN dpkg --add-architecture armhf
+RUN dpkg --add-architecture arm64
+RUN dpkg --add-architecture ppc64el
RUN apt-get update
# dkpg-dev: to make pkg-config work in cross-builds
+# llvm: for llvm-symbolizer, which is used by clang's UBSan for symbolized stack traces
RUN apt-get install --no-install-recommends --no-upgrade -y \
git ca-certificates \
make automake libtool pkg-config dpkg-dev valgrind qemu-user \
- gcc clang libc6-dbg \
- gcc-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 \
- gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x
+ gcc clang llvm libc6-dbg \
+ g++ \
+ gcc-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 libubsan1:i386 libasan5:i386 \
+ gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \
+ gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \
+ gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \
+ gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \
+ wine gcc-mingw-w64-x86-64
+
+# Run a dummy command in wine to make it set up configuration
+RUN wine64-stable xcopy || true
diff --git a/src/secp256k1/configure.ac b/src/secp256k1/configure.ac
index 1ed991afa7..9969cfa343 100644
--- a/src/secp256k1/configure.ac
+++ b/src/secp256k1/configure.ac
@@ -8,10 +8,6 @@ AH_TOP([#define LIBSECP256K1_CONFIG_H])
AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/])
AM_INIT_AUTOMAKE([foreign subdir-objects])
-# Set -g if CFLAGS are not already set, which matches the default autoconf
-# behavior (see PROG_CC in the Autoconf manual) with the exception that we don't
-# set -O2 here because we set it in any case (see further down).
-: ${CFLAGS="-g"}
LT_INIT
# Make the compilation flags quiet unless V=1 is used.
@@ -42,8 +38,8 @@ AM_PROG_AS
case $host_os in
*darwin*)
if test x$cross_compiling != xyes; then
- AC_PATH_PROG([BREW],brew,)
- if test x$BREW != x; then
+ AC_CHECK_PROG([BREW], brew, brew)
+ if test x$BREW = xbrew; then
# These Homebrew packages may be keg-only, meaning that they won't be found
# in expected paths because they may conflict with system files. Ask
# Homebrew where each one is located, then adjust paths accordingly.
@@ -58,10 +54,10 @@ case $host_os in
VALGRIND_CPPFLAGS="-I$valgrind_prefix/include"
fi
else
- AC_PATH_PROG([PORT],port,)
+ AC_CHECK_PROG([PORT], port, port)
# If homebrew isn't installed and macports is, add the macports default paths
# as a last resort.
- if test x$PORT != x; then
+ if test x$PORT = xport; then
CPPFLAGS="$CPPFLAGS -isystem /opt/local/include"
LDFLAGS="$LDFLAGS -L/opt/local/lib"
fi
@@ -70,35 +66,41 @@ case $host_os in
;;
esac
-CFLAGS="-W $CFLAGS"
-
-warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef -Wno-unused-function -Wno-long-long -Wno-overlength-strings"
-saved_CFLAGS="$CFLAGS"
-CFLAGS="$warn_CFLAGS $CFLAGS"
-AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}])
-AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
- [ AC_MSG_RESULT([yes]) ],
- [ AC_MSG_RESULT([no])
- CFLAGS="$saved_CFLAGS"
- ])
-
-saved_CFLAGS="$CFLAGS"
-CFLAGS="-Wconditional-uninitialized $CFLAGS"
-AC_MSG_CHECKING([if ${CC} supports -Wconditional-uninitialized])
-AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
- [ AC_MSG_RESULT([yes]) ],
- [ AC_MSG_RESULT([no])
- CFLAGS="$saved_CFLAGS"
- ])
-
-saved_CFLAGS="$CFLAGS"
-CFLAGS="-fvisibility=hidden $CFLAGS"
-AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden])
-AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
- [ AC_MSG_RESULT([yes]) ],
- [ AC_MSG_RESULT([no])
- CFLAGS="$saved_CFLAGS"
- ])
+# Try if some desirable compiler flags are supported and append them to SECP_CFLAGS.
+#
+# These are our own flags, so we append them to our own SECP_CFLAGS variable (instead of CFLAGS) as
+# recommended in the automake manual (Section "Flag Variables Ordering"). CFLAGS belongs to the user
+# and we are not supposed to touch it. In the Makefile, we will need to ensure that SECP_CFLAGS
+# is prepended to CFLAGS when invoking the compiler so that the user always has the last word (flag).
+#
+# Another advantage of not touching CFLAGS is that the contents of CFLAGS will be picked up by
+# libtool for compiling helper executables. For example, when compiling for Windows, libtool will
+# generate entire wrapper executables (instead of simple wrapper scripts as on Unix) to ensure
+# proper operation of uninstalled programs linked by libtool against the uninstalled shared library.
+# These executables are compiled from C source file for which our flags may not be appropriate,
+# e.g., -std=c89 flag has lead to undesirable warnings in the past.
+#
+# TODO We should analogously not touch CPPFLAGS and LDFLAGS but currently there are no issues.
+AC_DEFUN([SECP_TRY_APPEND_DEFAULT_CFLAGS], [
+ # Try to append -Werror=unknown-warning-option to CFLAGS temporarily. Otherwise clang will
+ # not error out if it gets unknown warning flags and the checks here will always succeed
+ # no matter if clang knows the flag or not.
+ SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS="$CFLAGS"
+ SECP_TRY_APPEND_CFLAGS([-Werror=unknown-warning-option], CFLAGS)
+
+ SECP_TRY_APPEND_CFLAGS([-std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef], $1) # GCC >= 3.0, -Wlong-long is implied by -pedantic.
+ SECP_TRY_APPEND_CFLAGS([-Wno-overlength-strings], $1) # GCC >= 4.2, -Woverlength-strings is implied by -pedantic.
+ SECP_TRY_APPEND_CFLAGS([-Wall], $1) # GCC >= 2.95 and probably many other compilers
+ SECP_TRY_APPEND_CFLAGS([-Wno-unused-function], $1) # GCC >= 3.0, -Wunused-function is implied by -Wall.
+ SECP_TRY_APPEND_CFLAGS([-Wextra], $1) # GCC >= 3.4, this is the newer name of -W, which we don't use because older GCCs will warn about unused functions.
+ SECP_TRY_APPEND_CFLAGS([-Wcast-align], $1) # GCC >= 2.95
+ SECP_TRY_APPEND_CFLAGS([-Wcast-align=strict], $1) # GCC >= 8.0
+ SECP_TRY_APPEND_CFLAGS([-Wconditional-uninitialized], $1) # Clang >= 3.0 only
+ SECP_TRY_APPEND_CFLAGS([-fvisibility=hidden], $1) # GCC >= 4.0
+
+ CFLAGS="$SECP_TRY_APPEND_DEFAULT_CFLAGS_saved_CFLAGS"
+])
+SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS)
###
### Define config arguments
@@ -213,10 +215,14 @@ AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
if test x"$enable_coverage" = x"yes"; then
AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
- CFLAGS="-O0 --coverage $CFLAGS"
+ SECP_CFLAGS="-O0 --coverage $SECP_CFLAGS"
LDFLAGS="--coverage $LDFLAGS"
else
- CFLAGS="-O2 $CFLAGS"
+ # Most likely the CFLAGS already contain -O2 because that is autoconf's default.
+ # We still add it here because passing it twice is not an issue, and handling
+ # this case would just add unnecessary complexity (see #896).
+ SECP_CFLAGS="-O2 $SECP_CFLAGS"
+ SECP_CFLAGS_FOR_BUILD="-O2 $SECP_CFLAGS_FOR_BUILD"
fi
if test x"$req_asm" = x"auto"; then
@@ -351,6 +357,9 @@ if test x"$enable_valgrind" = x"yes"; then
SECP_INCLUDES="$SECP_INCLUDES $VALGRIND_CPPFLAGS"
fi
+# Add -Werror and similar flags passed from the outside (for testing, e.g., in CI)
+SECP_CFLAGS="$SECP_CFLAGS $WERROR_CFLAGS"
+
# Handle static precomputation (after everything which modifies CFLAGS and friends)
if test x"$use_ecmult_static_precomputation" != x"no"; then
if test x"$cross_compiling" = x"no"; then
@@ -360,8 +369,9 @@ if test x"$use_ecmult_static_precomputation" != x"no"; then
fi
# If we're not cross-compiling, simply use the same compiler for building the static precompation code.
CC_FOR_BUILD="$CC"
- CFLAGS_FOR_BUILD="$CFLAGS"
CPPFLAGS_FOR_BUILD="$CPPFLAGS"
+ SECP_CFLAGS_FOR_BUILD="$SECP_CFLAGS"
+ CFLAGS_FOR_BUILD="$CFLAGS"
LDFLAGS_FOR_BUILD="$LDFLAGS"
else
AX_PROG_CC_FOR_BUILD
@@ -371,22 +381,14 @@ if test x"$use_ecmult_static_precomputation" != x"no"; then
cross_compiling=no
SAVE_CC="$CC"
CC="$CC_FOR_BUILD"
- SAVE_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS_FOR_BUILD"
SAVE_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS_FOR_BUILD"
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS_FOR_BUILD"
SAVE_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS_FOR_BUILD"
- warn_CFLAGS_FOR_BUILD="-Wall -Wextra -Wno-unused-function"
- saved_CFLAGS="$CFLAGS"
- CFLAGS="$warn_CFLAGS_FOR_BUILD $CFLAGS"
- AC_MSG_CHECKING([if native ${CC_FOR_BUILD} supports ${warn_CFLAGS_FOR_BUILD}])
- AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])],
- [ AC_MSG_RESULT([yes]) ],
- [ AC_MSG_RESULT([no])
- CFLAGS="$saved_CFLAGS"
- ])
+ SECP_TRY_APPEND_DEFAULT_CFLAGS(SECP_CFLAGS_FOR_BUILD)
AC_MSG_CHECKING([for working native compiler: ${CC_FOR_BUILD}])
AC_RUN_IFELSE(
@@ -394,19 +396,17 @@ if test x"$use_ecmult_static_precomputation" != x"no"; then
[working_native_cc=yes],
[working_native_cc=no],[:])
- CFLAGS_FOR_BUILD="$CFLAGS"
-
# Restore the environment
cross_compiling=$save_cross_compiling
CC="$SAVE_CC"
- CFLAGS="$SAVE_CFLAGS"
CPPFLAGS="$SAVE_CPPFLAGS"
+ CFLAGS="$SAVE_CFLAGS"
LDFLAGS="$SAVE_LDFLAGS"
if test x"$working_native_cc" = x"no"; then
AC_MSG_RESULT([no])
set_precomp=no
- m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CFLAGS_FOR_BUILD, CPPFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
+ m4_define([please_set_for_build], [Please set CC_FOR_BUILD, CPPFLAGS_FOR_BUILD, CFLAGS_FOR_BUILD, and/or LDFLAGS_FOR_BUILD.])
if test x"$use_ecmult_static_precomputation" = x"yes"; then
AC_MSG_ERROR([native compiler ${CC_FOR_BUILD} does not produce working binaries. please_set_for_build])
else
@@ -419,8 +419,9 @@ if test x"$use_ecmult_static_precomputation" != x"no"; then
fi
AC_SUBST(CC_FOR_BUILD)
- AC_SUBST(CFLAGS_FOR_BUILD)
AC_SUBST(CPPFLAGS_FOR_BUILD)
+ AC_SUBST(SECP_CFLAGS_FOR_BUILD)
+ AC_SUBST(CFLAGS_FOR_BUILD)
AC_SUBST(LDFLAGS_FOR_BUILD)
else
set_precomp=no
@@ -490,6 +491,7 @@ AC_SUBST(SECP_INCLUDES)
AC_SUBST(SECP_LIBS)
AC_SUBST(SECP_TEST_LIBS)
AC_SUBST(SECP_TEST_INCLUDES)
+AC_SUBST(SECP_CFLAGS)
AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"])
AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
@@ -532,13 +534,15 @@ fi
echo
echo " valgrind = $enable_valgrind"
echo " CC = $CC"
-echo " CFLAGS = $CFLAGS"
echo " CPPFLAGS = $CPPFLAGS"
+echo " SECP_CFLAGS = $SECP_CFLAGS"
+echo " CFLAGS = $CFLAGS"
echo " LDFLAGS = $LDFLAGS"
echo
if test x"$set_precomp" = x"yes"; then
echo " CC_FOR_BUILD = $CC_FOR_BUILD"
-echo " CFLAGS_FOR_BUILD = $CFLAGS_FOR_BUILD"
echo " CPPFLAGS_FOR_BUILD = $CPPFLAGS_FOR_BUILD"
+echo " SECP_CFLAGS_FOR_BUILD = $SECP_CFLAGS_FOR_BUILD"
+echo " CFLAGS_FOR_BUILD = $CFLAGS_FOR_BUILD"
echo " LDFLAGS_FOR_BUILD = $LDFLAGS_FOR_BUILD"
fi
diff --git a/src/secp256k1/contrib/lax_der_parsing.c b/src/secp256k1/contrib/lax_der_parsing.c
index c1627e37e9..bf562303ed 100644
--- a/src/secp256k1/contrib/lax_der_parsing.c
+++ b/src/secp256k1/contrib/lax_der_parsing.c
@@ -5,7 +5,6 @@
***********************************************************************/
#include <string.h>
-#include <secp256k1.h>
#include "lax_der_parsing.h"
@@ -121,7 +120,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
/* Copy R value */
if (rlen > 32) {
overflow = 1;
- } else {
+ } else if (rlen) {
memcpy(tmpsig + 32 - rlen, input + rpos, rlen);
}
@@ -133,7 +132,7 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_
/* Copy S value */
if (slen > 32) {
overflow = 1;
- } else {
+ } else if (slen) {
memcpy(tmpsig + 64 - slen, input + spos, slen);
}
diff --git a/src/secp256k1/contrib/lax_der_parsing.h b/src/secp256k1/contrib/lax_der_parsing.h
index 6b7255e28f..034a38e6a0 100644
--- a/src/secp256k1/contrib/lax_der_parsing.h
+++ b/src/secp256k1/contrib/lax_der_parsing.h
@@ -51,7 +51,13 @@
#ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H
#define SECP256K1_CONTRIB_LAX_DER_PARSING_H
+/* #include secp256k1.h only when it hasn't been included yet.
+ This enables this file to be #included directly in other project
+ files (such as tests.c) without the need to set an explicit -I flag,
+ which would be necessary to locate secp256k1.h. */
+#ifndef SECP256K1_H
#include <secp256k1.h>
+#endif
#ifdef __cplusplus
extern "C" {
diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.c b/src/secp256k1/contrib/lax_der_privatekey_parsing.c
index 429760fbb6..a1b8200079 100644
--- a/src/secp256k1/contrib/lax_der_privatekey_parsing.c
+++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.c
@@ -5,7 +5,6 @@
***********************************************************************/
#include <string.h>
-#include <secp256k1.h>
#include "lax_der_privatekey_parsing.h"
@@ -45,7 +44,7 @@ int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, co
if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) {
return 0;
}
- memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]);
+ if (privkey[1]) memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]);
if (!secp256k1_ec_seckey_verify(ctx, out32)) {
memset(out32, 0, 32);
return 0;
diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.h b/src/secp256k1/contrib/lax_der_privatekey_parsing.h
index 602c7c556a..1a8ad8ae0c 100644
--- a/src/secp256k1/contrib/lax_der_privatekey_parsing.h
+++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.h
@@ -28,7 +28,13 @@
#ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H
#define SECP256K1_CONTRIB_BER_PRIVATEKEY_H
+/* #include secp256k1.h only when it hasn't been included yet.
+ This enables this file to be #included directly in other project
+ files (such as tests.c) without the need to set an explicit -I flag,
+ which would be necessary to locate secp256k1.h. */
+#ifndef SECP256K1_H
#include <secp256k1.h>
+#endif
#ifdef __cplusplus
extern "C" {
diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h
index d368488af2..7be7fd5723 100644
--- a/src/secp256k1/include/secp256k1.h
+++ b/src/secp256k1/include/secp256k1.h
@@ -7,7 +7,9 @@ extern "C" {
#include <stddef.h>
-/* These rules specify the order of arguments in API calls:
+/* Unless explicitly stated all pointer arguments must not be NULL.
+ *
+ * The following rules specify the order of arguments in API calls:
*
* 1. Context pointers go first, followed by output arguments, combined
* output/input arguments, and finally input-only arguments.
@@ -61,8 +63,9 @@ typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space;
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
- * If you need to convert to a format suitable for storage, transmission, or
- * comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse.
+ * If you need to convert to a format suitable for storage or transmission,
+ * use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. To
+ * compare keys, use secp256k1_ec_pubkey_cmp.
*/
typedef struct {
unsigned char data[64];
@@ -127,6 +130,17 @@ typedef int (*secp256k1_nonce_function)(
# define SECP256K1_INLINE inline
# endif
+/** When this header is used at build-time the SECP256K1_BUILD define needs to be set
+ * to correctly setup export attributes and nullness checks. This is normally done
+ * by secp256k1.c but to guard against this header being included before secp256k1.c
+ * has had a chance to set the define (e.g. via test harnesses that just includes
+ * secp256k1.c) we set SECP256K1_NO_BUILD when this header is processed without the
+ * BUILD define so this condition can be caught.
+ */
+#ifndef SECP256K1_BUILD
+# define SECP256K1_NO_BUILD
+#endif
+
#ifndef SECP256K1_API
# if defined(_WIN32)
# ifdef SECP256K1_BUILD
@@ -370,6 +384,21 @@ SECP256K1_API int secp256k1_ec_pubkey_serialize(
unsigned int flags
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+/** Compare two public keys using lexicographic (of compressed serialization) order
+ *
+ * Returns: <0 if the first public key is less than the second
+ * >0 if the first public key is greater than the second
+ * 0 if the two public keys are equal
+ * Args: ctx: a secp256k1 context object.
+ * In: pubkey1: first public key to compare
+ * pubkey2: second public key to compare
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_cmp(
+ const secp256k1_context* ctx,
+ const secp256k1_pubkey* pubkey1,
+ const secp256k1_pubkey* pubkey2
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
/** Parse an ECDSA signature in compact (64 bytes) format.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
@@ -764,6 +793,31 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
size_t n
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+/** Compute a tagged hash as defined in BIP-340.
+ *
+ * This is useful for creating a message hash and achieving domain separation
+ * through an application-specific tag. This function returns
+ * SHA256(SHA256(tag)||SHA256(tag)||msg). Therefore, tagged hash
+ * implementations optimized for a specific tag can precompute the SHA256 state
+ * after hashing the tag hashes.
+ *
+ * Returns 0 if the arguments are invalid and 1 otherwise.
+ * Args: ctx: pointer to a context object
+ * Out: hash32: pointer to a 32-byte array to store the resulting hash
+ * In: tag: pointer to an array containing the tag
+ * taglen: length of the tag array
+ * msg: pointer to an array containing the message
+ * msglen: length of the message array
+ */
+SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_tagged_sha256(
+ const secp256k1_context* ctx,
+ unsigned char *hash32,
+ const unsigned char *tag,
+ size_t taglen,
+ const unsigned char *msg,
+ size_t msglen
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/secp256k1/include/secp256k1_extrakeys.h b/src/secp256k1/include/secp256k1_extrakeys.h
index 6fc7b290f8..0a37fb6b9d 100644
--- a/src/secp256k1/include/secp256k1_extrakeys.h
+++ b/src/secp256k1/include/secp256k1_extrakeys.h
@@ -15,9 +15,9 @@ extern "C" {
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
- * If you need to convert to a format suitable for storage, transmission, or
- * comparison, use secp256k1_xonly_pubkey_serialize and
- * secp256k1_xonly_pubkey_parse.
+ * If you need to convert to a format suitable for storage, transmission, use
+ * use secp256k1_xonly_pubkey_serialize and secp256k1_xonly_pubkey_parse. To
+ * compare keys, use secp256k1_xonly_pubkey_cmp.
*/
typedef struct {
unsigned char data[64];
@@ -67,6 +67,21 @@ SECP256K1_API int secp256k1_xonly_pubkey_serialize(
const secp256k1_xonly_pubkey* pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+/** Compare two x-only public keys using lexicographic order
+ *
+ * Returns: <0 if the first public key is less than the second
+ * >0 if the first public key is greater than the second
+ * 0 if the two public keys are equal
+ * Args: ctx: a secp256k1 context object.
+ * In: pubkey1: first public key to compare
+ * pubkey2: second public key to compare
+ */
+SECP256K1_API int secp256k1_xonly_pubkey_cmp(
+ const secp256k1_context* ctx,
+ const secp256k1_xonly_pubkey* pk1,
+ const secp256k1_xonly_pubkey* pk2
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
+
/** Converts a secp256k1_pubkey into a secp256k1_xonly_pubkey.
*
* Returns: 1 if the public key was successfully converted
diff --git a/src/secp256k1/include/secp256k1_schnorrsig.h b/src/secp256k1/include/secp256k1_schnorrsig.h
index 0150cd3395..74cbcac45e 100644
--- a/src/secp256k1/include/secp256k1_schnorrsig.h
+++ b/src/secp256k1/include/secp256k1_schnorrsig.h
@@ -23,24 +23,29 @@ extern "C" {
*
* Returns: 1 if a nonce was successfully generated. 0 will cause signing to
* return an error.
- * Out: nonce32: pointer to a 32-byte array to be filled by the function.
- * In: msg32: the 32-byte message hash being verified (will not be NULL)
- * key32: pointer to a 32-byte secret key (will not be NULL)
- * xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
- * (will not be NULL)
- * algo16: pointer to a 16-byte array describing the signature
- * algorithm (will not be NULL).
- * data: Arbitrary data pointer that is passed through.
+ * Out: nonce32: pointer to a 32-byte array to be filled by the function
+ * In: msg: the message being verified. Is NULL if and only if msglen
+ * is 0.
+ * msglen: the length of the message
+ * key32: pointer to a 32-byte secret key (will not be NULL)
+ * xonly_pk32: the 32-byte serialized xonly pubkey corresponding to key32
+ * (will not be NULL)
+ * algo: pointer to an array describing the signature
+ * algorithm (will not be NULL)
+ * algolen: the length of the algo array
+ * data: arbitrary data pointer that is passed through
*
* Except for test cases, this function should compute some cryptographic hash of
* the message, the key, the pubkey, the algorithm description, and data.
*/
typedef int (*secp256k1_nonce_function_hardened)(
unsigned char *nonce32,
- const unsigned char *msg32,
+ const unsigned char *msg,
+ size_t msglen,
const unsigned char *key32,
const unsigned char *xonly_pk32,
- const unsigned char *algo16,
+ const unsigned char *algo,
+ size_t algolen,
void *data
);
@@ -50,59 +55,113 @@ typedef int (*secp256k1_nonce_function_hardened)(
*
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
* auxiliary random data as defined in BIP-340. If the data pointer is NULL,
- * schnorrsig_sign does not produce BIP-340 compliant signatures. The algo16
- * argument must be non-NULL, otherwise the function will fail and return 0.
- * The hash will be tagged with algo16 after removing all terminating null
- * bytes. Therefore, to create BIP-340 compliant signatures, algo16 must be set
- * to "BIP0340/nonce\0\0\0"
+ * the nonce derivation procedure follows BIP-340 by setting the auxiliary
+ * random data to zero. The algo argument must be non-NULL, otherwise the
+ * function will fail and return 0. The hash will be tagged with algo.
+ * Therefore, to create BIP-340 compliant signatures, algo must be set to
+ * "BIP0340/nonce" and algolen to 13.
*/
SECP256K1_API extern const secp256k1_nonce_function_hardened secp256k1_nonce_function_bip340;
+/** Data structure that contains additional arguments for schnorrsig_sign_custom.
+ *
+ * A schnorrsig_extraparams structure object can be initialized correctly by
+ * setting it to SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT.
+ *
+ * Members:
+ * magic: set to SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC at initialization
+ * and has no other function than making sure the object is
+ * initialized.
+ * noncefp: pointer to a nonce generation function. If NULL,
+ * secp256k1_nonce_function_bip340 is used
+ * ndata: pointer to arbitrary data used by the nonce generation function
+ * (can be NULL). If it is non-NULL and
+ * secp256k1_nonce_function_bip340 is used, then ndata must be a
+ * pointer to 32-byte auxiliary randomness as per BIP-340.
+ */
+typedef struct {
+ unsigned char magic[4];
+ secp256k1_nonce_function_hardened noncefp;
+ void* ndata;
+} secp256k1_schnorrsig_extraparams;
+
+#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC { 0xda, 0x6f, 0xb3, 0x8c }
+#define SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT {\
+ SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC,\
+ NULL,\
+ NULL\
+}
+
/** Create a Schnorr signature.
*
* Does _not_ strictly follow BIP-340 because it does not verify the resulting
* signature. Instead, you can manually use secp256k1_schnorrsig_verify and
* abort if it fails.
*
- * Otherwise BIP-340 compliant if the noncefp argument is NULL or
- * secp256k1_nonce_function_bip340 and the ndata argument is 32-byte auxiliary
- * randomness.
+ * This function only signs 32-byte messages. If you have messages of a
+ * different size (or the same size but without a context-specific tag
+ * prefix), it is recommended to create a 32-byte message hash with
+ * secp256k1_tagged_sha256 and then sign the hash. Tagged hashing allows
+ * providing an context-specific tag for domain separation. This prevents
+ * signatures from being valid in multiple contexts by accident.
*
* Returns 1 on success, 0 on failure.
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: sig64: pointer to a 64-byte array to store the serialized signature (cannot be NULL)
* In: msg32: the 32-byte message being signed (cannot be NULL)
* keypair: pointer to an initialized keypair (cannot be NULL)
- * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bip340 is used
- * ndata: pointer to arbitrary data used by the nonce generation
- * function (can be NULL). If it is non-NULL and
- * secp256k1_nonce_function_bip340 is used, then ndata must be a
- * pointer to 32-byte auxiliary randomness as per BIP-340.
+ * aux_rand32: 32 bytes of fresh randomness. While recommended to provide
+ * this, it is only supplemental to security and can be NULL. See
+ * BIP-340 "Default Signing" for a full explanation of this
+ * argument and for guidance if randomness is expensive.
*/
SECP256K1_API int secp256k1_schnorrsig_sign(
const secp256k1_context* ctx,
unsigned char *sig64,
const unsigned char *msg32,
const secp256k1_keypair *keypair,
- secp256k1_nonce_function_hardened noncefp,
- void *ndata
+ unsigned char *aux_rand32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+/** Create a Schnorr signature with a more flexible API.
+ *
+ * Same arguments as secp256k1_schnorrsig_sign except that it allows signing
+ * variable length messages and accepts a pointer to an extraparams object that
+ * allows customizing signing by passing additional arguments.
+ *
+ * Creates the same signatures as schnorrsig_sign if msglen is 32 and the
+ * extraparams.ndata is the same as aux_rand32.
+ *
+ * In: msg: the message being signed. Can only be NULL if msglen is 0.
+ * msglen: length of the message
+ * extraparams: pointer to a extraparams object (can be NULL)
+ */
+SECP256K1_API int secp256k1_schnorrsig_sign_custom(
+ const secp256k1_context* ctx,
+ unsigned char *sig64,
+ const unsigned char *msg,
+ size_t msglen,
+ const secp256k1_keypair *keypair,
+ secp256k1_schnorrsig_extraparams *extraparams
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);
+
/** Verify a Schnorr signature.
*
* Returns: 1: correct signature
* 0: incorrect signature
* Args: ctx: a secp256k1 context object, initialized for verification.
* In: sig64: pointer to the 64-byte signature to verify (cannot be NULL)
- * msg32: the 32-byte message being verified (cannot be NULL)
+ * msg: the message being verified. Can only be NULL if msglen is 0.
+ * msglen: length of the message
* pubkey: pointer to an x-only public key to verify with (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify(
const secp256k1_context* ctx,
const unsigned char *sig64,
- const unsigned char *msg32,
+ const unsigned char *msg,
+ size_t msglen,
const secp256k1_xonly_pubkey *pubkey
-) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
+) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(5);
#ifdef __cplusplus
}
diff --git a/src/secp256k1/obj/.gitignore b/src/secp256k1/obj/.gitignore
deleted file mode 100644
index e69de29bb2..0000000000
--- a/src/secp256k1/obj/.gitignore
+++ /dev/null
diff --git a/src/secp256k1/src/bench_ecdh.c b/src/secp256k1/src/bench_ecdh.c
index ab4b8f4244..cb020d26b4 100644
--- a/src/secp256k1/src/bench_ecdh.c
+++ b/src/secp256k1/src/bench_ecdh.c
@@ -6,8 +6,8 @@
#include <string.h>
-#include "include/secp256k1.h"
-#include "include/secp256k1_ecdh.h"
+#include "../include/secp256k1.h"
+#include "../include/secp256k1_ecdh.h"
#include "util.h"
#include "bench.h"
diff --git a/src/secp256k1/src/bench_ecmult.c b/src/secp256k1/src/bench_ecmult.c
index 204e85a5dd..1d463f92d0 100644
--- a/src/secp256k1/src/bench_ecmult.c
+++ b/src/secp256k1/src/bench_ecmult.c
@@ -5,7 +5,8 @@
***********************************************************************/
#include <stdio.h>
-#include "include/secp256k1.h"
+#include "secp256k1.c"
+#include "../include/secp256k1.h"
#include "util.h"
#include "hash_impl.h"
@@ -14,33 +15,177 @@
#include "scalar_impl.h"
#include "ecmult_impl.h"
#include "bench.h"
-#include "secp256k1.c"
#define POINTS 32768
+void help(char **argv) {
+ printf("Benchmark EC multiplication algorithms\n");
+ printf("\n");
+ printf("Usage: %s <help|pippenger_wnaf|strauss_wnaf|simple>\n", argv[0]);
+ printf("The output shows the number of multiplied and summed points right after the\n");
+ printf("function name. The letter 'g' indicates that one of the points is the generator.\n");
+ printf("The benchmarks are divided by the number of points.\n");
+ printf("\n");
+ printf("default (ecmult_multi): picks pippenger_wnaf or strauss_wnaf depending on the\n");
+ printf(" batch size\n");
+ printf("pippenger_wnaf: for all batch sizes\n");
+ printf("strauss_wnaf: for all batch sizes\n");
+ printf("simple: multiply and sum each point individually\n");
+}
+
typedef struct {
/* Setup once in advance */
secp256k1_context* ctx;
secp256k1_scratch_space* scratch;
secp256k1_scalar* scalars;
secp256k1_ge* pubkeys;
+ secp256k1_gej* pubkeys_gej;
secp256k1_scalar* seckeys;
secp256k1_gej* expected_output;
secp256k1_ecmult_multi_func ecmult_multi;
- /* Changes per test */
+ /* Changes per benchmark */
size_t count;
int includes_g;
- /* Changes per test iteration */
+ /* Changes per benchmark iteration, used to pick different scalars and pubkeys
+ * in each run. */
size_t offset1;
size_t offset2;
- /* Test output. */
+ /* Benchmark output. */
secp256k1_gej* output;
} bench_data;
-static int bench_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, void* arg) {
+/* Hashes x into [0, POINTS) twice and store the result in offset1 and offset2. */
+static void hash_into_offset(bench_data* data, size_t x) {
+ data->offset1 = (x * 0x537b7f6f + 0x8f66a481) % POINTS;
+ data->offset2 = (x * 0x7f6f537b + 0x6a1a8f49) % POINTS;
+}
+
+/* Check correctness of the benchmark by computing
+ * sum(outputs) ?= (sum(scalars_gen) + sum(seckeys)*sum(scalars))*G */
+static void bench_ecmult_teardown_helper(bench_data* data, size_t* seckey_offset, size_t* scalar_offset, size_t* scalar_gen_offset, int iters) {
+ int i;
+ secp256k1_gej sum_output, tmp;
+ secp256k1_scalar sum_scalars;
+
+ secp256k1_gej_set_infinity(&sum_output);
+ secp256k1_scalar_clear(&sum_scalars);
+ for (i = 0; i < iters; ++i) {
+ secp256k1_gej_add_var(&sum_output, &sum_output, &data->output[i], NULL);
+ if (scalar_gen_offset != NULL) {
+ secp256k1_scalar_add(&sum_scalars, &sum_scalars, &data->scalars[(*scalar_gen_offset+i) % POINTS]);
+ }
+ if (seckey_offset != NULL) {
+ secp256k1_scalar s = data->seckeys[(*seckey_offset+i) % POINTS];
+ secp256k1_scalar_mul(&s, &s, &data->scalars[(*scalar_offset+i) % POINTS]);
+ secp256k1_scalar_add(&sum_scalars, &sum_scalars, &s);
+ }
+ }
+ secp256k1_ecmult_gen(&data->ctx->ecmult_gen_ctx, &tmp, &sum_scalars);
+ secp256k1_gej_neg(&tmp, &tmp);
+ secp256k1_gej_add_var(&tmp, &tmp, &sum_output, NULL);
+ CHECK(secp256k1_gej_is_infinity(&tmp));
+}
+
+static void bench_ecmult_setup(void* arg) {
+ bench_data* data = (bench_data*)arg;
+ /* Re-randomize offset to ensure that we're using different scalars and
+ * group elements in each run. */
+ hash_into_offset(data, data->offset1);
+}
+
+static void bench_ecmult_gen(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ int i;
+
+ for (i = 0; i < iters; ++i) {
+ secp256k1_ecmult_gen(&data->ctx->ecmult_gen_ctx, &data->output[i], &data->scalars[(data->offset1+i) % POINTS]);
+ }
+}
+
+static void bench_ecmult_gen_teardown(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ bench_ecmult_teardown_helper(data, NULL, NULL, &data->offset1, iters);
+}
+
+static void bench_ecmult_const(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ int i;
+
+ for (i = 0; i < iters; ++i) {
+ secp256k1_ecmult_const(&data->output[i], &data->pubkeys[(data->offset1+i) % POINTS], &data->scalars[(data->offset2+i) % POINTS], 256);
+ }
+}
+
+static void bench_ecmult_const_teardown(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ bench_ecmult_teardown_helper(data, &data->offset1, &data->offset2, NULL, iters);
+}
+
+static void bench_ecmult_1(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ int i;
+
+ for (i = 0; i < iters; ++i) {
+ secp256k1_ecmult(&data->ctx->ecmult_ctx, &data->output[i], &data->pubkeys_gej[(data->offset1+i) % POINTS], &data->scalars[(data->offset2+i) % POINTS], NULL);
+ }
+}
+
+static void bench_ecmult_1_teardown(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ bench_ecmult_teardown_helper(data, &data->offset1, &data->offset2, NULL, iters);
+}
+
+static void bench_ecmult_1g(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ secp256k1_scalar zero;
+ int i;
+
+ secp256k1_scalar_set_int(&zero, 0);
+ for (i = 0; i < iters; ++i) {
+ secp256k1_ecmult(&data->ctx->ecmult_ctx, &data->output[i], NULL, &zero, &data->scalars[(data->offset1+i) % POINTS]);
+ }
+}
+
+static void bench_ecmult_1g_teardown(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ bench_ecmult_teardown_helper(data, NULL, NULL, &data->offset1, iters);
+}
+
+static void bench_ecmult_2g(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ int i;
+
+ for (i = 0; i < iters/2; ++i) {
+ secp256k1_ecmult(&data->ctx->ecmult_ctx, &data->output[i], &data->pubkeys_gej[(data->offset1+i) % POINTS], &data->scalars[(data->offset2+i) % POINTS], &data->scalars[(data->offset1+i) % POINTS]);
+ }
+}
+
+static void bench_ecmult_2g_teardown(void* arg, int iters) {
+ bench_data* data = (bench_data*)arg;
+ bench_ecmult_teardown_helper(data, &data->offset1, &data->offset2, &data->offset1, iters/2);
+}
+
+static void run_ecmult_bench(bench_data* data, int iters) {
+ char str[32];
+ sprintf(str, "ecmult_gen");
+ run_benchmark(str, bench_ecmult_gen, bench_ecmult_setup, bench_ecmult_gen_teardown, data, 10, iters);
+ sprintf(str, "ecmult_const");
+ run_benchmark(str, bench_ecmult_const, bench_ecmult_setup, bench_ecmult_const_teardown, data, 10, iters);
+ /* ecmult with non generator point */
+ sprintf(str, "ecmult 1");
+ run_benchmark(str, bench_ecmult_1, bench_ecmult_setup, bench_ecmult_1_teardown, data, 10, iters);
+ /* ecmult with generator point */
+ sprintf(str, "ecmult 1g");
+ run_benchmark(str, bench_ecmult_1g, bench_ecmult_setup, bench_ecmult_1g_teardown, data, 10, iters);
+ /* ecmult with generator and non-generator point. The reported time is per point. */
+ sprintf(str, "ecmult 2g");
+ run_benchmark(str, bench_ecmult_2g, bench_ecmult_setup, bench_ecmult_2g_teardown, data, 10, 2*iters);
+}
+
+static int bench_ecmult_multi_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, void* arg) {
bench_data* data = (bench_data*)arg;
if (data->includes_g) ++idx;
if (idx == 0) {
@@ -53,7 +198,7 @@ static int bench_callback(secp256k1_scalar* sc, secp256k1_ge* ge, size_t idx, vo
return 1;
}
-static void bench_ecmult(void* arg, int iters) {
+static void bench_ecmult_multi(void* arg, int iters) {
bench_data* data = (bench_data*)arg;
int includes_g = data->includes_g;
@@ -62,19 +207,18 @@ static void bench_ecmult(void* arg, int iters) {
iters = iters / data->count;
for (iter = 0; iter < iters; ++iter) {
- data->ecmult_multi(&data->ctx->error_callback, &data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_callback, arg, count - includes_g);
+ data->ecmult_multi(&data->ctx->error_callback, &data->ctx->ecmult_ctx, data->scratch, &data->output[iter], data->includes_g ? &data->scalars[data->offset1] : NULL, bench_ecmult_multi_callback, arg, count - includes_g);
data->offset1 = (data->offset1 + count) % POINTS;
data->offset2 = (data->offset2 + count - 1) % POINTS;
}
}
-static void bench_ecmult_setup(void* arg) {
+static void bench_ecmult_multi_setup(void* arg) {
bench_data* data = (bench_data*)arg;
- data->offset1 = (data->count * 0x537b7f6f + 0x8f66a481) % POINTS;
- data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS;
+ hash_into_offset(data, data->count);
}
-static void bench_ecmult_teardown(void* arg, int iters) {
+static void bench_ecmult_multi_teardown(void* arg, int iters) {
bench_data* data = (bench_data*)arg;
int iter;
iters = iters / data->count;
@@ -88,7 +232,7 @@ static void bench_ecmult_teardown(void* arg, int iters) {
static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) {
secp256k1_sha256 sha256;
- unsigned char c[11] = {'e', 'c', 'm', 'u', 'l', 't', 0, 0, 0, 0};
+ unsigned char c[10] = {'e', 'c', 'm', 'u', 'l', 't', 0, 0, 0, 0};
unsigned char buf[32];
int overflow = 0;
c[6] = num;
@@ -102,7 +246,7 @@ static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) {
CHECK(!overflow);
}
-static void run_test(bench_data* data, size_t count, int includes_g, int num_iters) {
+static void run_ecmult_multi_bench(bench_data* data, size_t count, int includes_g, int num_iters) {
char str[32];
static const secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
size_t iters = 1 + num_iters / count;
@@ -112,8 +256,7 @@ static void run_test(bench_data* data, size_t count, int includes_g, int num_ite
data->includes_g = includes_g;
/* Compute (the negation of) the expected results directly. */
- data->offset1 = (data->count * 0x537b7f6f + 0x8f66a481) % POINTS;
- data->offset2 = (data->count * 0x7f6f537b + 0x6a1a8f49) % POINTS;
+ hash_into_offset(data, data->count);
for (iter = 0; iter < iters; ++iter) {
secp256k1_scalar tmp;
secp256k1_scalar total = data->scalars[(data->offset1++) % POINTS];
@@ -127,25 +270,26 @@ static void run_test(bench_data* data, size_t count, int includes_g, int num_ite
}
/* Run the benchmark. */
- sprintf(str, includes_g ? "ecmult_%ig" : "ecmult_%i", (int)count);
- run_benchmark(str, bench_ecmult, bench_ecmult_setup, bench_ecmult_teardown, data, 10, count * iters);
+ sprintf(str, includes_g ? "ecmult_multi %ig" : "ecmult_multi %i", (int)count);
+ run_benchmark(str, bench_ecmult_multi, bench_ecmult_multi_setup, bench_ecmult_multi_teardown, data, 10, count * iters);
}
int main(int argc, char **argv) {
bench_data data;
int i, p;
- secp256k1_gej* pubkeys_gej;
size_t scratch_size;
int iters = get_iters(10000);
- data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
- scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
- data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
data.ecmult_multi = secp256k1_ecmult_multi_var;
if (argc > 1) {
- if(have_flag(argc, argv, "pippenger_wnaf")) {
+ if(have_flag(argc, argv, "-h")
+ || have_flag(argc, argv, "--help")
+ || have_flag(argc, argv, "help")) {
+ help(argv);
+ return 1;
+ } else if(have_flag(argc, argv, "pippenger_wnaf")) {
printf("Using pippenger_wnaf:\n");
data.ecmult_multi = secp256k1_ecmult_pippenger_batch_single;
} else if(have_flag(argc, argv, "strauss_wnaf")) {
@@ -153,39 +297,48 @@ int main(int argc, char **argv) {
data.ecmult_multi = secp256k1_ecmult_strauss_batch_single;
} else if(have_flag(argc, argv, "simple")) {
printf("Using simple algorithm:\n");
- data.ecmult_multi = secp256k1_ecmult_multi_var;
- secp256k1_scratch_space_destroy(data.ctx, data.scratch);
- data.scratch = NULL;
} else {
- fprintf(stderr, "%s: unrecognized argument '%s'.\n", argv[0], argv[1]);
- fprintf(stderr, "Use 'pippenger_wnaf', 'strauss_wnaf', 'simple' or no argument to benchmark a combined algorithm.\n");
+ fprintf(stderr, "%s: unrecognized argument '%s'.\n\n", argv[0], argv[1]);
+ help(argv);
return 1;
}
}
+ data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ scratch_size = secp256k1_strauss_scratch_size(POINTS) + STRAUSS_SCRATCH_OBJECTS*16;
+ if (!have_flag(argc, argv, "simple")) {
+ data.scratch = secp256k1_scratch_space_create(data.ctx, scratch_size);
+ } else {
+ data.scratch = NULL;
+ }
+
/* Allocate stuff */
data.scalars = malloc(sizeof(secp256k1_scalar) * POINTS);
data.seckeys = malloc(sizeof(secp256k1_scalar) * POINTS);
data.pubkeys = malloc(sizeof(secp256k1_ge) * POINTS);
+ data.pubkeys_gej = malloc(sizeof(secp256k1_gej) * POINTS);
data.expected_output = malloc(sizeof(secp256k1_gej) * (iters + 1));
data.output = malloc(sizeof(secp256k1_gej) * (iters + 1));
/* Generate a set of scalars, and private/public keypairs. */
- pubkeys_gej = malloc(sizeof(secp256k1_gej) * POINTS);
- secp256k1_gej_set_ge(&pubkeys_gej[0], &secp256k1_ge_const_g);
+ secp256k1_gej_set_ge(&data.pubkeys_gej[0], &secp256k1_ge_const_g);
secp256k1_scalar_set_int(&data.seckeys[0], 1);
for (i = 0; i < POINTS; ++i) {
generate_scalar(i, &data.scalars[i]);
if (i) {
- secp256k1_gej_double_var(&pubkeys_gej[i], &pubkeys_gej[i - 1], NULL);
+ secp256k1_gej_double_var(&data.pubkeys_gej[i], &data.pubkeys_gej[i - 1], NULL);
secp256k1_scalar_add(&data.seckeys[i], &data.seckeys[i - 1], &data.seckeys[i - 1]);
}
}
- secp256k1_ge_set_all_gej_var(data.pubkeys, pubkeys_gej, POINTS);
- free(pubkeys_gej);
+ secp256k1_ge_set_all_gej_var(data.pubkeys, data.pubkeys_gej, POINTS);
+
+
+ /* Initialize offset1 and offset2 */
+ hash_into_offset(&data, 0);
+ run_ecmult_bench(&data, iters);
for (i = 1; i <= 8; ++i) {
- run_test(&data, i, 1, iters);
+ run_ecmult_multi_bench(&data, i, 1, iters);
}
/* This is disabled with low count of iterations because the loop runs 77 times even with iters=1
@@ -194,7 +347,7 @@ int main(int argc, char **argv) {
if (iters > 2) {
for (p = 0; p <= 11; ++p) {
for (i = 9; i <= 16; ++i) {
- run_test(&data, i << p, 1, iters);
+ run_ecmult_multi_bench(&data, i << p, 1, iters);
}
}
}
@@ -205,6 +358,7 @@ int main(int argc, char **argv) {
secp256k1_context_destroy(data.ctx);
free(data.scalars);
free(data.pubkeys);
+ free(data.pubkeys_gej);
free(data.seckeys);
free(data.output);
free(data.expected_output);
diff --git a/src/secp256k1/src/bench_internal.c b/src/secp256k1/src/bench_internal.c
index 73b8a24ccb..161b1c4a47 100644
--- a/src/secp256k1/src/bench_internal.c
+++ b/src/secp256k1/src/bench_internal.c
@@ -5,7 +5,8 @@
***********************************************************************/
#include <stdio.h>
-#include "include/secp256k1.h"
+#include "secp256k1.c"
+#include "../include/secp256k1.h"
#include "assumptions.h"
#include "util.h"
@@ -16,7 +17,6 @@
#include "ecmult_const_impl.h"
#include "ecmult_impl.h"
#include "bench.h"
-#include "secp256k1.c"
typedef struct {
secp256k1_scalar scalar[2];
diff --git a/src/secp256k1/src/bench_recover.c b/src/secp256k1/src/bench_recover.c
index 3f6270ce84..4bcac19dc0 100644
--- a/src/secp256k1/src/bench_recover.c
+++ b/src/secp256k1/src/bench_recover.c
@@ -4,8 +4,8 @@
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
-#include "include/secp256k1.h"
-#include "include/secp256k1_recovery.h"
+#include "../include/secp256k1.h"
+#include "../include/secp256k1_recovery.h"
#include "util.h"
#include "bench.h"
diff --git a/src/secp256k1/src/bench_schnorrsig.c b/src/secp256k1/src/bench_schnorrsig.c
index f7f591c41d..d95bc00f48 100644
--- a/src/secp256k1/src/bench_schnorrsig.c
+++ b/src/secp256k1/src/bench_schnorrsig.c
@@ -8,11 +8,13 @@
#include <stdlib.h>
-#include "include/secp256k1.h"
-#include "include/secp256k1_schnorrsig.h"
+#include "../include/secp256k1.h"
+#include "../include/secp256k1_schnorrsig.h"
#include "util.h"
#include "bench.h"
+#define MSGLEN 32
+
typedef struct {
secp256k1_context *ctx;
int n;
@@ -26,13 +28,13 @@ typedef struct {
void bench_schnorrsig_sign(void* arg, int iters) {
bench_schnorrsig_data *data = (bench_schnorrsig_data *)arg;
int i;
- unsigned char msg[32] = "benchmarkexamplemessagetemplate";
+ unsigned char msg[MSGLEN] = {0};
unsigned char sig[64];
for (i = 0; i < iters; i++) {
msg[0] = i;
msg[1] = i >> 8;
- CHECK(secp256k1_schnorrsig_sign(data->ctx, sig, msg, data->keypairs[i], NULL, NULL));
+ CHECK(secp256k1_schnorrsig_sign_custom(data->ctx, sig, msg, MSGLEN, data->keypairs[i], NULL));
}
}
@@ -43,7 +45,7 @@ void bench_schnorrsig_verify(void* arg, int iters) {
for (i = 0; i < iters; i++) {
secp256k1_xonly_pubkey pk;
CHECK(secp256k1_xonly_pubkey_parse(data->ctx, &pk, data->pk[i]) == 1);
- CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], &pk));
+ CHECK(secp256k1_schnorrsig_verify(data->ctx, data->sigs[i], data->msgs[i], MSGLEN, &pk));
}
}
@@ -58,9 +60,10 @@ int main(void) {
data.msgs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
data.sigs = (const unsigned char **)malloc(iters * sizeof(unsigned char *));
+ CHECK(MSGLEN >= 4);
for (i = 0; i < iters; i++) {
unsigned char sk[32];
- unsigned char *msg = (unsigned char *)malloc(32);
+ unsigned char *msg = (unsigned char *)malloc(MSGLEN);
unsigned char *sig = (unsigned char *)malloc(64);
secp256k1_keypair *keypair = (secp256k1_keypair *)malloc(sizeof(*keypair));
unsigned char *pk_char = (unsigned char *)malloc(32);
@@ -69,7 +72,7 @@ int main(void) {
msg[1] = sk[1] = i >> 8;
msg[2] = sk[2] = i >> 16;
msg[3] = sk[3] = i >> 24;
- memset(&msg[4], 'm', 28);
+ memset(&msg[4], 'm', MSGLEN - 4);
memset(&sk[4], 's', 28);
data.keypairs[i] = keypair;
@@ -78,7 +81,7 @@ int main(void) {
data.sigs[i] = sig;
CHECK(secp256k1_keypair_create(data.ctx, keypair, sk));
- CHECK(secp256k1_schnorrsig_sign(data.ctx, sig, msg, keypair, NULL, NULL));
+ CHECK(secp256k1_schnorrsig_sign_custom(data.ctx, sig, msg, MSGLEN, keypair, NULL));
CHECK(secp256k1_keypair_xonly_pub(data.ctx, &pk, NULL, keypair));
CHECK(secp256k1_xonly_pubkey_serialize(data.ctx, pk_char, &pk) == 1);
}
diff --git a/src/secp256k1/src/bench_sign.c b/src/secp256k1/src/bench_sign.c
index 933f367c4b..f659c18c92 100644
--- a/src/secp256k1/src/bench_sign.c
+++ b/src/secp256k1/src/bench_sign.c
@@ -4,7 +4,7 @@
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
-#include "include/secp256k1.h"
+#include "../include/secp256k1.h"
#include "util.h"
#include "bench.h"
diff --git a/src/secp256k1/src/bench_verify.c b/src/secp256k1/src/bench_verify.c
index c56aefd369..565ae4beec 100644
--- a/src/secp256k1/src/bench_verify.c
+++ b/src/secp256k1/src/bench_verify.c
@@ -7,7 +7,7 @@
#include <stdio.h>
#include <string.h>
-#include "include/secp256k1.h"
+#include "../include/secp256k1.h"
#include "util.h"
#include "bench.h"
diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h
index 156a33d112..c32141e887 100644
--- a/src/secp256k1/src/ecdsa_impl.h
+++ b/src/secp256k1/src/ecdsa_impl.h
@@ -140,7 +140,7 @@ static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char
overflow = 1;
}
if (!overflow) {
- memcpy(ra + 32 - rlen, *sig, rlen);
+ if (rlen) memcpy(ra + 32 - rlen, *sig, rlen);
secp256k1_scalar_set_b32(r, ra, &overflow);
}
if (overflow) {
diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h
index 7ab617e20e..84537bbfed 100644
--- a/src/secp256k1/src/ecmult.h
+++ b/src/secp256k1/src/ecmult.h
@@ -17,7 +17,6 @@ typedef struct {
secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */
} secp256k1_ecmult_context;
-static const size_t SECP256K1_ECMULT_CONTEXT_PREALLOCATED_SIZE;
static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx);
static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, void **prealloc);
static void secp256k1_ecmult_context_finalize_memcpy(secp256k1_ecmult_context *dst, const secp256k1_ecmult_context *src);
diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h
index 539618dcbb..05cf4d52cc 100644
--- a/src/secp256k1/src/ecmult_gen.h
+++ b/src/secp256k1/src/ecmult_gen.h
@@ -35,7 +35,6 @@ typedef struct {
secp256k1_gej initial;
} secp256k1_ecmult_gen_context;
-static const size_t SECP256K1_ECMULT_GEN_CONTEXT_PREALLOCATED_SIZE;
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx);
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, void **prealloc);
static void secp256k1_ecmult_gen_context_finalize_memcpy(secp256k1_ecmult_gen_context *dst, const secp256k1_ecmult_gen_context* src);
diff --git a/src/secp256k1/src/gen_context.c b/src/secp256k1/src/gen_context.c
index 024c557261..f9176eb996 100644
--- a/src/secp256k1/src/gen_context.c
+++ b/src/secp256k1/src/gen_context.c
@@ -13,7 +13,13 @@
/* We can't require the precomputed tables when creating them. */
#undef USE_ECMULT_STATIC_PRECOMPUTATION
-#include "include/secp256k1.h"
+/* In principle we could use ASM, but this yields only a minor speedup in
+ build time and it's very complicated. In particular when cross-compiling, we'd
+ need to build the ASM for the build and the host machine. */
+#undef USE_EXTERNAL_ASM
+#undef USE_ASM_X86_64
+
+#include "../include/secp256k1.h"
#include "assumptions.h"
#include "util.h"
#include "field_impl.h"
diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h
index 19ebd8f44e..47aea32be1 100644
--- a/src/secp256k1/src/group_impl.h
+++ b/src/secp256k1/src/group_impl.h
@@ -100,8 +100,8 @@ static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) {
static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) {
secp256k1_fe z2, z3;
- r->infinity = a->infinity;
if (a->infinity) {
+ secp256k1_ge_set_infinity(r);
return;
}
secp256k1_fe_inv_var(&a->z, &a->z);
@@ -110,8 +110,7 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) {
secp256k1_fe_mul(&a->x, &a->x, &z2);
secp256k1_fe_mul(&a->y, &a->y, &z3);
secp256k1_fe_set_int(&a->z, 1);
- r->x = a->x;
- r->y = a->y;
+ secp256k1_ge_set_xy(r, &a->x, &a->y);
}
static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) {
@@ -120,7 +119,9 @@ static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a
size_t last_i = SIZE_MAX;
for (i = 0; i < len; i++) {
- if (!a[i].infinity) {
+ if (a[i].infinity) {
+ secp256k1_ge_set_infinity(&r[i]);
+ } else {
/* Use destination's x coordinates as scratch space */
if (last_i == SIZE_MAX) {
r[i].x = a[i].z;
@@ -148,7 +149,6 @@ static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a
r[last_i].x = u;
for (i = 0; i < len; i++) {
- r[i].infinity = a[i].infinity;
if (!a[i].infinity) {
secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x);
}
@@ -311,7 +311,7 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
* point will be gibberish (z = 0 but infinity = 0).
*/
if (a->infinity) {
- r->infinity = 1;
+ secp256k1_gej_set_infinity(r);
if (rzr != NULL) {
secp256k1_fe_set_int(rzr, 1);
}
diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h
index 1ac67086be..5408c9de70 100644
--- a/src/secp256k1/src/modules/ecdh/main_impl.h
+++ b/src/secp256k1/src/modules/ecdh/main_impl.h
@@ -7,8 +7,8 @@
#ifndef SECP256K1_MODULE_ECDH_MAIN_H
#define SECP256K1_MODULE_ECDH_MAIN_H
-#include "include/secp256k1_ecdh.h"
-#include "ecmult_const_impl.h"
+#include "../../../include/secp256k1_ecdh.h"
+#include "../../ecmult_const_impl.h"
static int ecdh_hash_function_sha256(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) {
unsigned char version = (y32[31] & 0x01) | 0x02;
diff --git a/src/secp256k1/src/modules/extrakeys/main_impl.h b/src/secp256k1/src/modules/extrakeys/main_impl.h
index 7390b22718..8607bbede7 100644
--- a/src/secp256k1/src/modules/extrakeys/main_impl.h
+++ b/src/secp256k1/src/modules/extrakeys/main_impl.h
@@ -7,8 +7,8 @@
#ifndef SECP256K1_MODULE_EXTRAKEYS_MAIN_H
#define SECP256K1_MODULE_EXTRAKEYS_MAIN_H
-#include "include/secp256k1.h"
-#include "include/secp256k1_extrakeys.h"
+#include "../../../include/secp256k1.h"
+#include "../../../include/secp256k1_extrakeys.h"
static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) {
return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey);
@@ -55,6 +55,32 @@ int secp256k1_xonly_pubkey_serialize(const secp256k1_context* ctx, unsigned char
return 1;
}
+int secp256k1_xonly_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_xonly_pubkey* pk0, const secp256k1_xonly_pubkey* pk1) {
+ unsigned char out[2][32];
+ const secp256k1_xonly_pubkey* pk[2];
+ int i;
+
+ VERIFY_CHECK(ctx != NULL);
+ pk[0] = pk0; pk[1] = pk1;
+ for (i = 0; i < 2; i++) {
+ /* If the public key is NULL or invalid, xonly_pubkey_serialize will
+ * call the illegal_callback and return 0. In that case we will
+ * serialize the key as all zeros which is less than any valid public
+ * key. This results in consistent comparisons even if NULL or invalid
+ * pubkeys are involved and prevents edge cases such as sorting
+ * algorithms that use this function and do not terminate as a
+ * result. */
+ if (!secp256k1_xonly_pubkey_serialize(ctx, out[i], pk[i])) {
+ /* Note that xonly_pubkey_serialize should already set the output to
+ * zero in that case, but it's not guaranteed by the API, we can't
+ * test it and writing a VERIFY_CHECK is more complex than
+ * explicitly memsetting (again). */
+ memset(out[i], 0, sizeof(out[i]));
+ }
+ }
+ return secp256k1_memcmp_var(out[0], out[1], sizeof(out[1]));
+}
+
/** Keeps a group element as is if it has an even Y and otherwise negates it.
* y_parity is set to 0 in the former case and to 1 in the latter case.
* Requires that the coordinates of r are normalized. */
diff --git a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
index 0aca4fb72d..d4a2f5bdf4 100644
--- a/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
+++ b/src/secp256k1/src/modules/extrakeys/tests_exhaustive_impl.h
@@ -8,7 +8,7 @@
#define SECP256K1_MODULE_EXTRAKEYS_TESTS_EXHAUSTIVE_H
#include "src/modules/extrakeys/main_impl.h"
-#include "include/secp256k1_extrakeys.h"
+#include "../../../include/secp256k1_extrakeys.h"
static void test_exhaustive_extrakeys(const secp256k1_context *ctx, const secp256k1_ge* group) {
secp256k1_keypair keypair[EXHAUSTIVE_TEST_ORDER - 1];
diff --git a/src/secp256k1/src/modules/extrakeys/tests_impl.h b/src/secp256k1/src/modules/extrakeys/tests_impl.h
index 9473a7dd48..4a5952714c 100644
--- a/src/secp256k1/src/modules/extrakeys/tests_impl.h
+++ b/src/secp256k1/src/modules/extrakeys/tests_impl.h
@@ -7,7 +7,7 @@
#ifndef SECP256K1_MODULE_EXTRAKEYS_TESTS_H
#define SECP256K1_MODULE_EXTRAKEYS_TESTS_H
-#include "secp256k1_extrakeys.h"
+#include "../../../include/secp256k1_extrakeys.h"
static secp256k1_context* api_test_context(int flags, int *ecount) {
secp256k1_context *ctx0 = secp256k1_context_create(flags);
@@ -137,6 +137,43 @@ void test_xonly_pubkey(void) {
secp256k1_context_destroy(verify);
}
+void test_xonly_pubkey_comparison(void) {
+ unsigned char pk1_ser[32] = {
+ 0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11,
+ 0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23
+ };
+ const unsigned char pk2_ser[32] = {
+ 0xde, 0x36, 0x0e, 0x87, 0x59, 0x8f, 0x3c, 0x01, 0x36, 0x2a, 0x2a, 0xb8, 0xc6, 0xf4, 0x5e, 0x4d,
+ 0xb2, 0xc2, 0xd5, 0x03, 0xa7, 0xf9, 0xf1, 0x4f, 0xa8, 0xfa, 0x95, 0xa8, 0xe9, 0x69, 0x76, 0x1c
+ };
+ secp256k1_xonly_pubkey pk1;
+ secp256k1_xonly_pubkey pk2;
+ int ecount = 0;
+ secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
+
+ CHECK(secp256k1_xonly_pubkey_parse(none, &pk1, pk1_ser) == 1);
+ CHECK(secp256k1_xonly_pubkey_parse(none, &pk2, pk2_ser) == 1);
+
+ CHECK(secp256k1_xonly_pubkey_cmp(none, NULL, &pk2) < 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, NULL) > 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, &pk2) < 0);
+ CHECK(secp256k1_xonly_pubkey_cmp(none, &pk2, &pk1) > 0);
+ CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, &pk1) == 0);
+ CHECK(secp256k1_xonly_pubkey_cmp(none, &pk2, &pk2) == 0);
+ CHECK(ecount == 2);
+ memset(&pk1, 0, sizeof(pk1)); /* illegal pubkey */
+ CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, &pk2) < 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_xonly_pubkey_cmp(none, &pk1, &pk1) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_xonly_pubkey_cmp(none, &pk2, &pk1) > 0);
+ CHECK(ecount == 6);
+
+ secp256k1_context_destroy(none);
+}
+
void test_xonly_pubkey_tweak(void) {
unsigned char zeros64[64] = { 0 };
unsigned char overflows[32];
@@ -540,6 +577,7 @@ void run_extrakeys_tests(void) {
test_xonly_pubkey_tweak();
test_xonly_pubkey_tweak_check();
test_xonly_pubkey_tweak_recursive();
+ test_xonly_pubkey_comparison();
/* keypair tests */
test_keypair();
diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h
index 7a440a729b..9e19f2a2dc 100644
--- a/src/secp256k1/src/modules/recovery/main_impl.h
+++ b/src/secp256k1/src/modules/recovery/main_impl.h
@@ -7,7 +7,7 @@
#ifndef SECP256K1_MODULE_RECOVERY_MAIN_H
#define SECP256K1_MODULE_RECOVERY_MAIN_H
-#include "include/secp256k1_recovery.h"
+#include "../../../include/secp256k1_recovery.h"
static void secp256k1_ecdsa_recoverable_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const secp256k1_ecdsa_recoverable_signature* sig) {
(void)ctx;
diff --git a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
index 0ba9409c69..590a972ed3 100644
--- a/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
+++ b/src/secp256k1/src/modules/recovery/tests_exhaustive_impl.h
@@ -8,7 +8,7 @@
#define SECP256K1_MODULE_RECOVERY_EXHAUSTIVE_TESTS_H
#include "src/modules/recovery/main_impl.h"
-#include "include/secp256k1_recovery.h"
+#include "../../../include/secp256k1_recovery.h"
void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group) {
int i, j, k;
diff --git a/src/secp256k1/src/modules/schnorrsig/main_impl.h b/src/secp256k1/src/modules/schnorrsig/main_impl.h
index 22e1b33a5a..693b78f034 100644
--- a/src/secp256k1/src/modules/schnorrsig/main_impl.h
+++ b/src/secp256k1/src/modules/schnorrsig/main_impl.h
@@ -7,9 +7,9 @@
#ifndef SECP256K1_MODULE_SCHNORRSIG_MAIN_H
#define SECP256K1_MODULE_SCHNORRSIG_MAIN_H
-#include "include/secp256k1.h"
-#include "include/secp256k1_schnorrsig.h"
-#include "hash.h"
+#include "../../../include/secp256k1.h"
+#include "../../../include/secp256k1_schnorrsig.h"
+#include "../../hash.h"
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
* SHA256 to SHA256("BIP0340/nonce")||SHA256("BIP0340/nonce"). */
@@ -43,16 +43,18 @@ static void secp256k1_nonce_function_bip340_sha256_tagged_aux(secp256k1_sha256 *
sha->bytes = 64;
}
-/* algo16 argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340
+/* algo argument for nonce_function_bip340 to derive the nonce exactly as stated in BIP-340
* by using the correct tagged hash function. */
-static const unsigned char bip340_algo16[16] = "BIP0340/nonce\0\0\0";
+static const unsigned char bip340_algo[13] = "BIP0340/nonce";
-static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
+static const unsigned char schnorrsig_extraparams_magic[4] = SECP256K1_SCHNORRSIG_EXTRAPARAMS_MAGIC;
+
+static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) {
secp256k1_sha256 sha;
unsigned char masked_key[32];
int i;
- if (algo16 == NULL) {
+ if (algo == NULL) {
return 0;
}
@@ -65,18 +67,14 @@ static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *ms
}
}
- /* Tag the hash with algo16 which is important to avoid nonce reuse across
+ /* Tag the hash with algo which is important to avoid nonce reuse across
* algorithms. If this nonce function is used in BIP-340 signing as defined
* in the spec, an optimized tagging implementation is used. */
- if (secp256k1_memcmp_var(algo16, bip340_algo16, 16) == 0) {
+ if (algolen == sizeof(bip340_algo)
+ && secp256k1_memcmp_var(algo, bip340_algo, algolen) == 0) {
secp256k1_nonce_function_bip340_sha256_tagged(&sha);
} else {
- int algo16_len = 16;
- /* Remove terminating null bytes */
- while (algo16_len > 0 && !algo16[algo16_len - 1]) {
- algo16_len--;
- }
- secp256k1_sha256_initialize_tagged(&sha, algo16, algo16_len);
+ secp256k1_sha256_initialize_tagged(&sha, algo, algolen);
}
/* Hash (masked-)key||pk||msg using the tagged hash as per the spec */
@@ -86,7 +84,7 @@ static int nonce_function_bip340(unsigned char *nonce32, const unsigned char *ms
secp256k1_sha256_write(&sha, key32, 32);
}
secp256k1_sha256_write(&sha, xonly_pk32, 32);
- secp256k1_sha256_write(&sha, msg32, 32);
+ secp256k1_sha256_write(&sha, msg, msglen);
secp256k1_sha256_finalize(&sha, nonce32);
return 1;
}
@@ -108,23 +106,23 @@ static void secp256k1_schnorrsig_sha256_tagged(secp256k1_sha256 *sha) {
sha->bytes = 64;
}
-static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned char *r32, const unsigned char *msg32, const unsigned char *pubkey32)
+static void secp256k1_schnorrsig_challenge(secp256k1_scalar* e, const unsigned char *r32, const unsigned char *msg, size_t msglen, const unsigned char *pubkey32)
{
unsigned char buf[32];
secp256k1_sha256 sha;
- /* tagged hash(r.x, pk.x, msg32) */
+ /* tagged hash(r.x, pk.x, msg) */
secp256k1_schnorrsig_sha256_tagged(&sha);
secp256k1_sha256_write(&sha, r32, 32);
secp256k1_sha256_write(&sha, pubkey32, 32);
- secp256k1_sha256_write(&sha, msg32, 32);
+ secp256k1_sha256_write(&sha, msg, msglen);
secp256k1_sha256_finalize(&sha, buf);
/* Set scalar e to the challenge hash modulo the curve order as per
* BIP340. */
secp256k1_scalar_set_b32(e, buf, NULL);
}
-int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) {
+int secp256k1_schnorrsig_sign_internal(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_nonce_function_hardened noncefp, void *ndata) {
secp256k1_scalar sk;
secp256k1_scalar e;
secp256k1_scalar k;
@@ -139,7 +137,7 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(sig64 != NULL);
- ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(msg != NULL || msglen == 0);
ARG_CHECK(keypair != NULL);
if (noncefp == NULL) {
@@ -156,7 +154,7 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64
secp256k1_scalar_get_b32(seckey, &sk);
secp256k1_fe_get_b32(pk_buf, &pk.x);
- ret &= !!noncefp(buf, msg32, seckey, pk_buf, bip340_algo16, ndata);
+ ret &= !!noncefp(buf, msg, msglen, seckey, pk_buf, bip340_algo, sizeof(bip340_algo), ndata);
secp256k1_scalar_set_b32(&k, buf, NULL);
ret &= !secp256k1_scalar_is_zero(&k);
secp256k1_scalar_cmov(&k, &secp256k1_scalar_one, !ret);
@@ -174,7 +172,7 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64
secp256k1_fe_normalize_var(&r.x);
secp256k1_fe_get_b32(&sig64[0], &r.x);
- secp256k1_schnorrsig_challenge(&e, &sig64[0], msg32, pk_buf);
+ secp256k1_schnorrsig_challenge(&e, &sig64[0], msg, msglen, pk_buf);
secp256k1_scalar_mul(&e, &e, &sk);
secp256k1_scalar_add(&e, &e, &k);
secp256k1_scalar_get_b32(&sig64[32], &e);
@@ -187,7 +185,26 @@ int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64
return ret;
}
-int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_xonly_pubkey *pubkey) {
+int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const secp256k1_keypair *keypair, unsigned char *aux_rand32) {
+ return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg32, 32, keypair, secp256k1_nonce_function_bip340, aux_rand32);
+}
+
+int secp256k1_schnorrsig_sign_custom(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_keypair *keypair, secp256k1_schnorrsig_extraparams *extraparams) {
+ secp256k1_nonce_function_hardened noncefp = NULL;
+ void *ndata = NULL;
+ VERIFY_CHECK(ctx != NULL);
+
+ if (extraparams != NULL) {
+ ARG_CHECK(secp256k1_memcmp_var(extraparams->magic,
+ schnorrsig_extraparams_magic,
+ sizeof(extraparams->magic)) == 0);
+ noncefp = extraparams->noncefp;
+ ndata = extraparams->ndata;
+ }
+ return secp256k1_schnorrsig_sign_internal(ctx, sig64, msg, msglen, keypair, noncefp, ndata);
+}
+
+int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg, size_t msglen, const secp256k1_xonly_pubkey *pubkey) {
secp256k1_scalar s;
secp256k1_scalar e;
secp256k1_gej rj;
@@ -201,7 +218,7 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(sig64 != NULL);
- ARG_CHECK(msg32 != NULL);
+ ARG_CHECK(msg != NULL || msglen == 0);
ARG_CHECK(pubkey != NULL);
if (!secp256k1_fe_set_b32(&rx, &sig64[0])) {
@@ -219,7 +236,7 @@ int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const unsigned cha
/* Compute e. */
secp256k1_fe_get_b32(buf, &pk.x);
- secp256k1_schnorrsig_challenge(&e, &sig64[0], msg32, buf);
+ secp256k1_schnorrsig_challenge(&e, &sig64[0], msg, msglen, buf);
/* Compute rj = s*G + (-e)*pkj */
secp256k1_scalar_negate(&e, &e);
diff --git a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
index b4a428729f..d8df9dd2df 100644
--- a/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
+++ b/src/secp256k1/src/modules/schnorrsig/tests_exhaustive_impl.h
@@ -7,7 +7,7 @@
#ifndef SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H
#define SECP256K1_MODULE_SCHNORRSIG_TESTS_EXHAUSTIVE_H
-#include "include/secp256k1_schnorrsig.h"
+#include "../../../include/secp256k1_schnorrsig.h"
#include "src/modules/schnorrsig/main_impl.h"
static const unsigned char invalid_pubkey_bytes[][32] = {
@@ -58,15 +58,19 @@ static const unsigned char invalid_pubkey_bytes[][32] = {
#define NUM_INVALID_KEYS (sizeof(invalid_pubkey_bytes) / sizeof(invalid_pubkey_bytes[0]))
-static int secp256k1_hardened_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32,
+static int secp256k1_hardened_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg,
+ size_t msglen,
const unsigned char *key32, const unsigned char *xonly_pk32,
- const unsigned char *algo16, void* data) {
+ const unsigned char *algo, size_t algolen,
+ void* data) {
secp256k1_scalar s;
int *idata = data;
- (void)msg32;
+ (void)msg;
+ (void)msglen;
(void)key32;
(void)xonly_pk32;
- (void)algo16;
+ (void)algo;
+ (void)algolen;
secp256k1_scalar_set_int(&s, *idata);
secp256k1_scalar_get_b32(nonce32, &s);
return 1;
@@ -101,7 +105,7 @@ static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, cons
secp256k1_scalar e;
unsigned char msg32[32];
secp256k1_testrand256(msg32);
- secp256k1_schnorrsig_challenge(&e, sig64, msg32, pk32);
+ secp256k1_schnorrsig_challenge(&e, sig64, msg32, sizeof(msg32), pk32);
/* Only do work if we hit a challenge we haven't tried before. */
if (!e_done[e]) {
/* Iterate over the possible valid last 32 bytes in the signature.
@@ -119,7 +123,7 @@ static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, cons
secp256k1_testrand256(sig64 + 32);
expect_valid = 0;
}
- valid = secp256k1_schnorrsig_verify(ctx, sig64, msg32, &pubkeys[d - 1]);
+ valid = secp256k1_schnorrsig_verify(ctx, sig64, msg32, sizeof(msg32), &pubkeys[d - 1]);
CHECK(valid == expect_valid);
count_valid += valid;
}
@@ -137,6 +141,8 @@ static void test_exhaustive_schnorrsig_verify(const secp256k1_context *ctx, cons
static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsigned char (*xonly_pubkey_bytes)[32], const secp256k1_keypair* keypairs, const int* parities) {
int d, k;
uint64_t iter = 0;
+ secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT;
+
/* Loop over keys. */
for (d = 1; d < EXHAUSTIVE_TEST_ORDER; ++d) {
int actual_d = d;
@@ -149,19 +155,21 @@ static void test_exhaustive_schnorrsig_sign(const secp256k1_context *ctx, unsign
unsigned char sig64[64];
int actual_k = k;
if (skip_section(&iter)) continue;
+ extraparams.noncefp = secp256k1_hardened_nonce_function_smallint;
+ extraparams.ndata = &k;
if (parities[k - 1]) actual_k = EXHAUSTIVE_TEST_ORDER - k;
/* Generate random messages until all challenges have been tried. */
while (e_count_done < EXHAUSTIVE_TEST_ORDER) {
secp256k1_scalar e;
secp256k1_testrand256(msg32);
- secp256k1_schnorrsig_challenge(&e, xonly_pubkey_bytes[k - 1], msg32, xonly_pubkey_bytes[d - 1]);
+ secp256k1_schnorrsig_challenge(&e, xonly_pubkey_bytes[k - 1], msg32, sizeof(msg32), xonly_pubkey_bytes[d - 1]);
/* Only do work if we hit a challenge we haven't tried before. */
if (!e_done[e]) {
secp256k1_scalar expected_s = (actual_k + e * actual_d) % EXHAUSTIVE_TEST_ORDER;
unsigned char expected_s_bytes[32];
secp256k1_scalar_get_b32(expected_s_bytes, &expected_s);
/* Invoke the real function to construct a signature. */
- CHECK(secp256k1_schnorrsig_sign(ctx, sig64, msg32, &keypairs[d - 1], secp256k1_hardened_nonce_function_smallint, &k));
+ CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig64, msg32, sizeof(msg32), &keypairs[d - 1], &extraparams));
/* The first 32 bytes must match the xonly pubkey for the specified k. */
CHECK(secp256k1_memcmp_var(sig64, xonly_pubkey_bytes[k - 1], 32) == 0);
/* The last 32 bytes must match the expected s value. */
diff --git a/src/secp256k1/src/modules/schnorrsig/tests_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
index 338462fc9d..59357afa99 100644
--- a/src/secp256k1/src/modules/schnorrsig/tests_impl.h
+++ b/src/secp256k1/src/modules/schnorrsig/tests_impl.h
@@ -7,16 +7,16 @@
#ifndef SECP256K1_MODULE_SCHNORRSIG_TESTS_H
#define SECP256K1_MODULE_SCHNORRSIG_TESTS_H
-#include "secp256k1_schnorrsig.h"
+#include "../../../include/secp256k1_schnorrsig.h"
/* Checks that a bit flip in the n_flip-th argument (that has n_bytes many
* bytes) changes the hash function
*/
-void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes) {
+void nonce_function_bip340_bitflip(unsigned char **args, size_t n_flip, size_t n_bytes, size_t msglen, size_t algolen) {
unsigned char nonces[2][32];
- CHECK(nonce_function_bip340(nonces[0], args[0], args[1], args[2], args[3], args[4]) == 1);
+ CHECK(nonce_function_bip340(nonces[0], args[0], msglen, args[1], args[2], args[3], algolen, args[4]) == 1);
secp256k1_testrand_flip(args[n_flip], n_bytes);
- CHECK(nonce_function_bip340(nonces[1], args[0], args[1], args[2], args[3], args[4]) == 1);
+ CHECK(nonce_function_bip340(nonces[1], args[0], msglen, args[1], args[2], args[3], algolen, args[4]) == 1);
CHECK(secp256k1_memcmp_var(nonces[0], nonces[1], 32) != 0);
}
@@ -34,11 +34,13 @@ void test_sha256_eq(const secp256k1_sha256 *sha1, const secp256k1_sha256 *sha2)
void run_nonce_function_bip340_tests(void) {
unsigned char tag[13] = "BIP0340/nonce";
unsigned char aux_tag[11] = "BIP0340/aux";
- unsigned char algo16[16] = "BIP0340/nonce\0\0\0";
+ unsigned char algo[13] = "BIP0340/nonce";
+ size_t algolen = sizeof(algo);
secp256k1_sha256 sha;
secp256k1_sha256 sha_optimized;
unsigned char nonce[32];
unsigned char msg[32];
+ size_t msglen = sizeof(msg);
unsigned char key[32];
unsigned char pk[32];
unsigned char aux_rand[32];
@@ -68,33 +70,45 @@ void run_nonce_function_bip340_tests(void) {
args[0] = msg;
args[1] = key;
args[2] = pk;
- args[3] = algo16;
+ args[3] = algo;
args[4] = aux_rand;
for (i = 0; i < count; i++) {
- nonce_function_bip340_bitflip(args, 0, 32);
- nonce_function_bip340_bitflip(args, 1, 32);
- nonce_function_bip340_bitflip(args, 2, 32);
- /* Flip algo16 special case "BIP0340/nonce" */
- nonce_function_bip340_bitflip(args, 3, 16);
- /* Flip algo16 again */
- nonce_function_bip340_bitflip(args, 3, 16);
- nonce_function_bip340_bitflip(args, 4, 32);
+ nonce_function_bip340_bitflip(args, 0, 32, msglen, algolen);
+ nonce_function_bip340_bitflip(args, 1, 32, msglen, algolen);
+ nonce_function_bip340_bitflip(args, 2, 32, msglen, algolen);
+ /* Flip algo special case "BIP0340/nonce" */
+ nonce_function_bip340_bitflip(args, 3, algolen, msglen, algolen);
+ /* Flip algo again */
+ nonce_function_bip340_bitflip(args, 3, algolen, msglen, algolen);
+ nonce_function_bip340_bitflip(args, 4, 32, msglen, algolen);
}
- /* NULL algo16 is disallowed */
- CHECK(nonce_function_bip340(nonce, msg, key, pk, NULL, NULL) == 0);
- /* Empty algo16 is fine */
- memset(algo16, 0x00, 16);
- CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
- /* algo16 with terminating null bytes is fine */
- algo16[1] = 65;
- CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
- /* Other algo16 is fine */
- memset(algo16, 0xFF, 16);
- CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+ /* NULL algo is disallowed */
+ CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, NULL, 0, NULL) == 0);
+ CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1);
+ /* Other algo is fine */
+ secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, algo, algolen);
+ CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1);
+
+ for (i = 0; i < count; i++) {
+ unsigned char nonce2[32];
+ uint32_t offset = secp256k1_testrand_int(msglen - 1);
+ size_t msglen_tmp = (msglen + offset) % msglen;
+ size_t algolen_tmp;
+
+ /* Different msglen gives different nonce */
+ CHECK(nonce_function_bip340(nonce2, msg, msglen_tmp, key, pk, algo, algolen, NULL) == 1);
+ CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0);
+
+ /* Different algolen gives different nonce */
+ offset = secp256k1_testrand_int(algolen - 1);
+ algolen_tmp = (algolen + offset) % algolen;
+ CHECK(nonce_function_bip340(nonce2, msg, msglen, key, pk, algo, algolen_tmp, NULL) == 1);
+ CHECK(secp256k1_memcmp_var(nonce, nonce2, 32) != 0);
+ }
/* NULL aux_rand argument is allowed. */
- CHECK(nonce_function_bip340(nonce, msg, key, pk, algo16, NULL) == 1);
+ CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1);
}
void test_schnorrsig_api(void) {
@@ -103,10 +117,12 @@ void test_schnorrsig_api(void) {
unsigned char sk3[32];
unsigned char msg[32];
secp256k1_keypair keypairs[3];
- secp256k1_keypair invalid_keypair = { 0 };
+ secp256k1_keypair invalid_keypair = {{ 0 }};
secp256k1_xonly_pubkey pk[3];
secp256k1_xonly_pubkey zero_pk;
unsigned char sig[64];
+ secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT;
+ secp256k1_schnorrsig_extraparams invalid_extraparams = {{ 0 }, NULL, NULL};
/** setup **/
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
@@ -138,36 +154,60 @@ void test_schnorrsig_api(void) {
/** main test body **/
ecount = 0;
- CHECK(secp256k1_schnorrsig_sign(none, sig, msg, &keypairs[0], NULL, NULL) == 0);
+ CHECK(secp256k1_schnorrsig_sign(none, sig, msg, &keypairs[0], NULL) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_schnorrsig_sign(vrfy, sig, msg, &keypairs[0], NULL) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL) == 1);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, &keypairs[0], NULL) == 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, NULL, &keypairs[0], NULL) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, NULL, NULL) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &invalid_keypair, NULL) == 0);
+ CHECK(ecount == 6);
+
+ ecount = 0;
+ CHECK(secp256k1_schnorrsig_sign_custom(none, sig, msg, sizeof(msg), &keypairs[0], &extraparams) == 0);
CHECK(ecount == 1);
- CHECK(secp256k1_schnorrsig_sign(vrfy, sig, msg, &keypairs[0], NULL, NULL) == 0);
+ CHECK(secp256k1_schnorrsig_sign_custom(vrfy, sig, msg, sizeof(msg), &keypairs[0], &extraparams) == 0);
CHECK(ecount == 2);
- CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1);
+ CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, msg, sizeof(msg), &keypairs[0], &extraparams) == 1);
CHECK(ecount == 2);
- CHECK(secp256k1_schnorrsig_sign(sign, NULL, msg, &keypairs[0], NULL, NULL) == 0);
+ CHECK(secp256k1_schnorrsig_sign_custom(sign, NULL, msg, sizeof(msg), &keypairs[0], &extraparams) == 0);
CHECK(ecount == 3);
- CHECK(secp256k1_schnorrsig_sign(sign, sig, NULL, &keypairs[0], NULL, NULL) == 0);
+ CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, NULL, sizeof(msg), &keypairs[0], &extraparams) == 0);
CHECK(ecount == 4);
- CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, NULL, NULL, NULL) == 0);
+ CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, NULL, 0, &keypairs[0], &extraparams) == 1);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, msg, sizeof(msg), NULL, &extraparams) == 0);
CHECK(ecount == 5);
- CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &invalid_keypair, NULL, NULL) == 0);
+ CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, msg, sizeof(msg), &invalid_keypair, &extraparams) == 0);
+ CHECK(ecount == 6);
+ CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, msg, sizeof(msg), &keypairs[0], NULL) == 1);
CHECK(ecount == 6);
+ CHECK(secp256k1_schnorrsig_sign_custom(sign, sig, msg, sizeof(msg), &keypairs[0], &invalid_extraparams) == 0);
+ CHECK(ecount == 7);
ecount = 0;
- CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL, NULL) == 1);
- CHECK(secp256k1_schnorrsig_verify(none, sig, msg, &pk[0]) == 0);
+ CHECK(secp256k1_schnorrsig_sign(sign, sig, msg, &keypairs[0], NULL) == 1);
+ CHECK(secp256k1_schnorrsig_verify(none, sig, msg, sizeof(msg), &pk[0]) == 0);
CHECK(ecount == 1);
- CHECK(secp256k1_schnorrsig_verify(sign, sig, msg, &pk[0]) == 0);
+ CHECK(secp256k1_schnorrsig_verify(sign, sig, msg, sizeof(msg), &pk[0]) == 0);
CHECK(ecount == 2);
- CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &pk[0]) == 1);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, sizeof(msg), &pk[0]) == 1);
CHECK(ecount == 2);
- CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, sizeof(msg), &pk[0]) == 0);
CHECK(ecount == 3);
- CHECK(secp256k1_schnorrsig_verify(vrfy, sig, NULL, &pk[0]) == 0);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, NULL, sizeof(msg), &pk[0]) == 0);
+ CHECK(ecount == 4);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, NULL, 0, &pk[0]) == 0);
CHECK(ecount == 4);
- CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, NULL) == 0);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, sizeof(msg), NULL) == 0);
CHECK(ecount == 5);
- CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, &zero_pk) == 0);
+ CHECK(secp256k1_schnorrsig_verify(vrfy, sig, msg, sizeof(msg), &zero_pk) == 0);
CHECK(ecount == 6);
secp256k1_context_destroy(none);
@@ -179,7 +219,7 @@ void test_schnorrsig_api(void) {
/* Checks that hash initialized by secp256k1_schnorrsig_sha256_tagged has the
* expected state. */
void test_schnorrsig_sha256_tagged(void) {
- char tag[17] = "BIP0340/challenge";
+ unsigned char tag[17] = "BIP0340/challenge";
secp256k1_sha256 sha;
secp256k1_sha256 sha_optimized;
@@ -190,19 +230,19 @@ void test_schnorrsig_sha256_tagged(void) {
/* Helper function for schnorrsig_bip_vectors
* Signs the message and checks that it's the same as expected_sig. */
-void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, unsigned char *aux_rand, const unsigned char *msg, const unsigned char *expected_sig) {
+void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, unsigned char *aux_rand, const unsigned char *msg32, const unsigned char *expected_sig) {
unsigned char sig[64];
secp256k1_keypair keypair;
secp256k1_xonly_pubkey pk, pk_expected;
CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
- CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, aux_rand));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg32, &keypair, aux_rand));
CHECK(secp256k1_memcmp_var(sig, expected_sig, 64) == 0);
CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk_expected, pk_serialized));
CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
CHECK(secp256k1_memcmp_var(&pk, &pk_expected, sizeof(pk)) == 0);
- CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &pk));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg32, 32, &pk));
}
/* Helper function for schnorrsig_bip_vectors
@@ -211,7 +251,7 @@ void test_schnorrsig_bip_vectors_check_verify(const unsigned char *pk_serialized
secp256k1_xonly_pubkey pk;
CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk, pk_serialized));
- CHECK(expected == secp256k1_schnorrsig_verify(ctx, sig, msg32, &pk));
+ CHECK(expected == secp256k1_schnorrsig_verify(ctx, sig, msg32, 32, &pk));
}
/* Test vectors according to BIP-340 ("Schnorr Signatures for secp256k1"). See
@@ -634,22 +674,26 @@ void test_schnorrsig_bip_vectors(void) {
}
/* Nonce function that returns constant 0 */
-static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
- (void) msg32;
+static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) {
+ (void) msg;
+ (void) msglen;
(void) key32;
(void) xonly_pk32;
- (void) algo16;
+ (void) algo;
+ (void) algolen;
(void) data;
(void) nonce32;
return 0;
}
/* Nonce function that sets nonce to 0 */
-static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
- (void) msg32;
+static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) {
+ (void) msg;
+ (void) msglen;
(void) key32;
(void) xonly_pk32;
- (void) algo16;
+ (void) algo;
+ (void) algolen;
(void) data;
memset(nonce32, 0, 32);
@@ -657,11 +701,13 @@ static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32,
}
/* Nonce function that sets nonce to 0xFF...0xFF */
-static int nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo16, void *data) {
- (void) msg32;
+static int nonce_function_overflowing(unsigned char *nonce32, const unsigned char *msg, size_t msglen, const unsigned char *key32, const unsigned char *xonly_pk32, const unsigned char *algo, size_t algolen, void *data) {
+ (void) msg;
+ (void) msglen;
(void) key32;
(void) xonly_pk32;
- (void) algo16;
+ (void) algo;
+ (void) algolen;
(void) data;
memset(nonce32, 0xFF, 32);
@@ -670,24 +716,45 @@ static int nonce_function_overflowing(unsigned char *nonce32, const unsigned cha
void test_schnorrsig_sign(void) {
unsigned char sk[32];
+ secp256k1_xonly_pubkey pk;
secp256k1_keypair keypair;
const unsigned char msg[32] = "this is a msg for a schnorrsig..";
unsigned char sig[64];
+ unsigned char sig2[64];
unsigned char zeros64[64] = { 0 };
+ secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT;
+ unsigned char aux_rand[32];
secp256k1_testrand256(sk);
+ secp256k1_testrand256(aux_rand);
CHECK(secp256k1_keypair_create(ctx, &keypair, sk));
- CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1);
+ CHECK(secp256k1_keypair_xonly_pub(ctx, &pk, NULL, &keypair));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL) == 1);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk));
/* Test different nonce functions */
+ CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk));
memset(sig, 1, sizeof(sig));
- CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_failing, NULL) == 0);
+ extraparams.noncefp = nonce_function_failing;
+ CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 0);
CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0);
memset(&sig, 1, sizeof(sig));
- CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_0, NULL) == 0);
+ extraparams.noncefp = nonce_function_0;
+ CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 0);
CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) == 0);
- CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, nonce_function_overflowing, NULL) == 1);
- CHECK(secp256k1_memcmp_var(sig, zeros64, sizeof(sig)) != 0);
+ memset(&sig, 1, sizeof(sig));
+ extraparams.noncefp = nonce_function_overflowing;
+ CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &pk));
+
+ /* When using the default nonce function, schnorrsig_sign_custom produces
+ * the same result as schnorrsig_sign with aux_rand = extraparams.ndata */
+ extraparams.noncefp = NULL;
+ extraparams.ndata = aux_rand;
+ CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig, msg, sizeof(msg), &keypair, &extraparams) == 1);
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig2, msg, &keypair, extraparams.ndata) == 1);
+ CHECK(secp256k1_memcmp_var(sig, sig2, sizeof(sig)) == 0);
}
#define N_SIGS 3
@@ -709,8 +776,8 @@ void test_schnorrsig_sign_verify(void) {
for (i = 0; i < N_SIGS; i++) {
secp256k1_testrand256(msg[i]);
- CHECK(secp256k1_schnorrsig_sign(ctx, sig[i], msg[i], &keypair, NULL, NULL));
- CHECK(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], &pk));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig[i], msg[i], &keypair, NULL));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[i], msg[i], sizeof(msg[i]), &pk));
}
{
@@ -720,36 +787,54 @@ void test_schnorrsig_sign_verify(void) {
size_t byte_idx = secp256k1_testrand_int(32);
unsigned char xorbyte = secp256k1_testrand_int(254)+1;
sig[sig_idx][byte_idx] ^= xorbyte;
- CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk));
sig[sig_idx][byte_idx] ^= xorbyte;
byte_idx = secp256k1_testrand_int(32);
sig[sig_idx][32+byte_idx] ^= xorbyte;
- CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk));
sig[sig_idx][32+byte_idx] ^= xorbyte;
byte_idx = secp256k1_testrand_int(32);
msg[sig_idx][byte_idx] ^= xorbyte;
- CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk));
msg[sig_idx][byte_idx] ^= xorbyte;
/* Check that above bitflips have been reversed correctly */
- CHECK(secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], &pk));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk));
}
/* Test overflowing s */
- CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL));
- CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk));
memset(&sig[0][32], 0xFF, 32);
- CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk));
/* Test negative s */
- CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL, NULL));
- CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig[0], msg[0], &keypair, NULL));
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk));
secp256k1_scalar_set_b32(&s, &sig[0][32], NULL);
secp256k1_scalar_negate(&s, &s);
secp256k1_scalar_get_b32(&sig[0][32], &s);
- CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], &pk));
+ CHECK(!secp256k1_schnorrsig_verify(ctx, sig[0], msg[0], sizeof(msg[0]), &pk));
+
+ /* The empty message can be signed & verified */
+ CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig[0], NULL, 0, &keypair, NULL) == 1);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], NULL, 0, &pk) == 1);
+
+ {
+ /* Test varying message lengths */
+ unsigned char msg_large[32 * 8];
+ uint32_t msglen = secp256k1_testrand_int(sizeof(msg_large));
+ for (i = 0; i < sizeof(msg_large); i += 32) {
+ secp256k1_testrand256(&msg_large[i]);
+ }
+ CHECK(secp256k1_schnorrsig_sign_custom(ctx, sig[0], msg_large, msglen, &keypair, NULL) == 1);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg_large, msglen, &pk) == 1);
+ /* Verification for a random wrong message length fails */
+ msglen = (msglen + (sizeof(msg_large) - 1)) % sizeof(msg_large);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig[0], msg_large, msglen, &pk) == 0);
+ }
}
#undef N_SIGS
@@ -777,10 +862,10 @@ void test_schnorrsig_taproot(void) {
/* Key spend */
secp256k1_testrand256(msg);
- CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL) == 1);
+ CHECK(secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL) == 1);
/* Verify key spend */
CHECK(secp256k1_xonly_pubkey_parse(ctx, &output_pk, output_pk_bytes) == 1);
- CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, &output_pk) == 1);
+ CHECK(secp256k1_schnorrsig_verify(ctx, sig, msg, sizeof(msg), &output_pk) == 1);
/* Script spend */
CHECK(secp256k1_xonly_pubkey_serialize(ctx, internal_pk_bytes, &internal_pk) == 1);
diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c
index aef3f99ac3..9908cab864 100644
--- a/src/secp256k1/src/secp256k1.c
+++ b/src/secp256k1/src/secp256k1.c
@@ -4,8 +4,10 @@
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
***********************************************************************/
-#include "include/secp256k1.h"
-#include "include/secp256k1_preallocated.h"
+#define SECP256K1_BUILD
+
+#include "../include/secp256k1.h"
+#include "../include/secp256k1_preallocated.h"
#include "assumptions.h"
#include "util.h"
@@ -21,6 +23,10 @@
#include "scratch_impl.h"
#include "selftest.h"
+#ifdef SECP256K1_NO_BUILD
+# error "secp256k1.h processed without SECP256K1_BUILD defined while building secp256k1.c"
+#endif
+
#if defined(VALGRIND)
# include <valgrind/memcheck.h>
#endif
@@ -316,6 +322,32 @@ int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *o
return ret;
}
+int secp256k1_ec_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_pubkey* pubkey0, const secp256k1_pubkey* pubkey1) {
+ unsigned char out[2][33];
+ const secp256k1_pubkey* pk[2];
+ int i;
+
+ VERIFY_CHECK(ctx != NULL);
+ pk[0] = pubkey0; pk[1] = pubkey1;
+ for (i = 0; i < 2; i++) {
+ size_t out_size = sizeof(out[i]);
+ /* If the public key is NULL or invalid, ec_pubkey_serialize will call
+ * the illegal_callback and return 0. In that case we will serialize the
+ * key as all zeros which is less than any valid public key. This
+ * results in consistent comparisons even if NULL or invalid pubkeys are
+ * involved and prevents edge cases such as sorting algorithms that use
+ * this function and do not terminate as a result. */
+ if (!secp256k1_ec_pubkey_serialize(ctx, out[i], &out_size, pk[i], SECP256K1_EC_COMPRESSED)) {
+ /* Note that ec_pubkey_serialize should already set the output to
+ * zero in that case, but it's not guaranteed by the API, we can't
+ * test it and writing a VERIFY_CHECK is more complex than
+ * explicitly memsetting (again). */
+ memset(out[i], 0, sizeof(out[i]));
+ }
+ }
+ return secp256k1_memcmp_var(out[0], out[1], sizeof(out[0]));
+}
+
static void secp256k1_ecdsa_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_ecdsa_signature* sig) {
(void)ctx;
if (sizeof(secp256k1_scalar) == 32) {
@@ -758,6 +790,19 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
return 1;
}
+int secp256k1_tagged_sha256(const secp256k1_context* ctx, unsigned char *hash32, const unsigned char *tag, size_t taglen, const unsigned char *msg, size_t msglen) {
+ secp256k1_sha256 sha;
+ VERIFY_CHECK(ctx != NULL);
+ ARG_CHECK(hash32 != NULL);
+ ARG_CHECK(tag != NULL);
+ ARG_CHECK(msg != NULL);
+
+ secp256k1_sha256_initialize_tagged(&sha, tag, taglen);
+ secp256k1_sha256_write(&sha, msg, msglen);
+ secp256k1_sha256_finalize(&sha, hash32);
+ return 1;
+}
+
#ifdef ENABLE_MODULE_ECDH
# include "modules/ecdh/main_impl.h"
#endif
diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h
index e643778f36..c8d30ef6a8 100644
--- a/src/secp256k1/src/testrand_impl.h
+++ b/src/secp256k1/src/testrand_impl.h
@@ -127,7 +127,7 @@ static void secp256k1_testrand_init(const char* hexseed) {
pos++;
}
} else {
- FILE *frand = fopen("/dev/urandom", "r");
+ FILE *frand = fopen("/dev/urandom", "rb");
if ((frand == NULL) || fread(&seed16, 1, sizeof(seed16), frand) != sizeof(seed16)) {
uint64_t t = time(NULL) * (uint64_t)1337;
fprintf(stderr, "WARNING: could not read 16 bytes from /dev/urandom; falling back to insecure PRNG\n");
diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c
index a146394305..99d9468e29 100644
--- a/src/secp256k1/src/tests.c
+++ b/src/secp256k1/src/tests.c
@@ -15,8 +15,8 @@
#include <time.h>
#include "secp256k1.c"
-#include "include/secp256k1.h"
-#include "include/secp256k1_preallocated.h"
+#include "../include/secp256k1.h"
+#include "../include/secp256k1_preallocated.h"
#include "testrand_impl.h"
#include "util.h"
@@ -30,8 +30,8 @@ void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
# endif
#endif
-#include "contrib/lax_der_parsing.c"
-#include "contrib/lax_der_privatekey_parsing.c"
+#include "../contrib/lax_der_parsing.c"
+#include "../contrib/lax_der_privatekey_parsing.c"
#include "modinv32_impl.h"
#ifdef SECP256K1_WIDEMUL_INT128
@@ -564,6 +564,38 @@ void run_rfc6979_hmac_sha256_tests(void) {
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
}
+void run_tagged_sha256_tests(void) {
+ int ecount = 0;
+ secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
+ unsigned char tag[32] = { 0 };
+ unsigned char msg[32] = { 0 };
+ unsigned char hash32[32];
+ unsigned char hash_expected[32] = {
+ 0x04, 0x7A, 0x5E, 0x17, 0xB5, 0x86, 0x47, 0xC1,
+ 0x3C, 0xC6, 0xEB, 0xC0, 0xAA, 0x58, 0x3B, 0x62,
+ 0xFB, 0x16, 0x43, 0x32, 0x68, 0x77, 0x40, 0x6C,
+ 0xE2, 0x76, 0x55, 0x9A, 0x3B, 0xDE, 0x55, 0xB3
+ };
+
+ secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
+
+ /* API test */
+ CHECK(secp256k1_tagged_sha256(none, hash32, tag, sizeof(tag), msg, sizeof(msg)) == 1);
+ CHECK(secp256k1_tagged_sha256(none, NULL, tag, sizeof(tag), msg, sizeof(msg)) == 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_tagged_sha256(none, hash32, NULL, 0, msg, sizeof(msg)) == 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_tagged_sha256(none, hash32, tag, sizeof(tag), NULL, 0) == 0);
+ CHECK(ecount == 3);
+
+ /* Static test vector */
+ memcpy(tag, "tag", 3);
+ memcpy(msg, "msg", 3);
+ CHECK(secp256k1_tagged_sha256(none, hash32, tag, 3, msg, 3) == 1);
+ CHECK(secp256k1_memcmp_var(hash32, hash_expected, sizeof(hash32)) == 0);
+ secp256k1_context_destroy(none);
+}
+
/***** RANDOM TESTS *****/
void test_rand_bits(int rand32, int bits) {
@@ -2508,6 +2540,70 @@ void run_field_misc(void) {
}
}
+void test_fe_mul(const secp256k1_fe* a, const secp256k1_fe* b, int use_sqr)
+{
+ secp256k1_fe c, an, bn;
+ /* Variables in BE 32-byte format. */
+ unsigned char a32[32], b32[32], c32[32];
+ /* Variables in LE 16x uint16_t format. */
+ uint16_t a16[16], b16[16], c16[16];
+ /* Field modulus in LE 16x uint16_t format. */
+ static const uint16_t m16[16] = {
+ 0xfc2f, 0xffff, 0xfffe, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ };
+ uint16_t t16[32];
+ int i;
+
+ /* Compute C = A * B in fe format. */
+ c = *a;
+ if (use_sqr) {
+ secp256k1_fe_sqr(&c, &c);
+ } else {
+ secp256k1_fe_mul(&c, &c, b);
+ }
+
+ /* Convert A, B, C into LE 16x uint16_t format. */
+ an = *a;
+ bn = *b;
+ secp256k1_fe_normalize_var(&c);
+ secp256k1_fe_normalize_var(&an);
+ secp256k1_fe_normalize_var(&bn);
+ secp256k1_fe_get_b32(a32, &an);
+ secp256k1_fe_get_b32(b32, &bn);
+ secp256k1_fe_get_b32(c32, &c);
+ for (i = 0; i < 16; ++i) {
+ a16[i] = a32[31 - 2*i] + ((uint16_t)a32[30 - 2*i] << 8);
+ b16[i] = b32[31 - 2*i] + ((uint16_t)b32[30 - 2*i] << 8);
+ c16[i] = c32[31 - 2*i] + ((uint16_t)c32[30 - 2*i] << 8);
+ }
+ /* Compute T = A * B in LE 16x uint16_t format. */
+ mulmod256(t16, a16, b16, m16);
+ /* Compare */
+ CHECK(secp256k1_memcmp_var(t16, c16, 32) == 0);
+}
+
+void run_fe_mul(void) {
+ int i;
+ for (i = 0; i < 100 * count; ++i) {
+ secp256k1_fe a, b, c, d;
+ random_fe(&a);
+ random_field_element_magnitude(&a);
+ random_fe(&b);
+ random_field_element_magnitude(&b);
+ random_fe_test(&c);
+ random_field_element_magnitude(&c);
+ random_fe_test(&d);
+ random_field_element_magnitude(&d);
+ test_fe_mul(&a, &a, 1);
+ test_fe_mul(&c, &c, 1);
+ test_fe_mul(&a, &b, 0);
+ test_fe_mul(&a, &c, 0);
+ test_fe_mul(&c, &b, 0);
+ test_fe_mul(&c, &d, 0);
+ }
+}
+
void run_sqr(void) {
secp256k1_fe x, s;
@@ -2595,7 +2691,7 @@ void test_inverse_scalar(secp256k1_scalar* out, const secp256k1_scalar* x, int v
{
secp256k1_scalar l, r, t;
- (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&l, x); /* l = 1/x */
+ (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse)(&l, x); /* l = 1/x */
if (out) *out = l;
if (secp256k1_scalar_is_zero(x)) {
CHECK(secp256k1_scalar_is_zero(&l));
@@ -2605,9 +2701,9 @@ void test_inverse_scalar(secp256k1_scalar* out, const secp256k1_scalar* x, int v
CHECK(secp256k1_scalar_is_one(&t)); /* x*(1/x) == 1 */
secp256k1_scalar_add(&r, x, &scalar_minus_one); /* r = x-1 */
if (secp256k1_scalar_is_zero(&r)) return;
- (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&r, &r); /* r = 1/(x-1) */
+ (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse)(&r, &r); /* r = 1/(x-1) */
secp256k1_scalar_add(&l, &scalar_minus_one, &l); /* l = 1/x-1 */
- (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse_var)(&l, &l); /* l = 1/(1/x-1) */
+ (var ? secp256k1_scalar_inverse_var : secp256k1_scalar_inverse)(&l, &l); /* l = 1/(1/x-1) */
secp256k1_scalar_add(&l, &l, &secp256k1_scalar_one); /* l = 1/(1/x-1)+1 */
secp256k1_scalar_add(&l, &r, &l); /* l = 1/(1/x-1)+1 + 1/(x-1) */
CHECK(secp256k1_scalar_is_zero(&l)); /* l == 0 */
@@ -3101,20 +3197,34 @@ void test_ge(void) {
/* Test batch gej -> ge conversion with many infinities. */
for (i = 0; i < 4 * runs + 1; i++) {
+ int odd;
random_group_element_test(&ge[i]);
+ odd = secp256k1_fe_is_odd(&ge[i].x);
+ CHECK(odd == 0 || odd == 1);
/* randomly set half the points to infinity */
- if(secp256k1_fe_is_odd(&ge[i].x)) {
+ if (odd == i % 2) {
secp256k1_ge_set_infinity(&ge[i]);
}
secp256k1_gej_set_ge(&gej[i], &ge[i]);
}
- /* batch invert */
+ /* batch convert */
secp256k1_ge_set_all_gej_var(ge, gej, 4 * runs + 1);
/* check result */
for (i = 0; i < 4 * runs + 1; i++) {
ge_equals_gej(&ge[i], &gej[i]);
}
+ /* Test batch gej -> ge conversion with all infinities. */
+ for (i = 0; i < 4 * runs + 1; i++) {
+ secp256k1_gej_set_infinity(&gej[i]);
+ }
+ /* batch convert */
+ secp256k1_ge_set_all_gej_var(ge, gej, 4 * runs + 1);
+ /* check result */
+ for (i = 0; i < 4 * runs + 1; i++) {
+ CHECK(secp256k1_ge_is_infinity(&ge[i]));
+ }
+
free(ge);
free(gej);
}
@@ -5434,6 +5544,55 @@ void test_random_pubkeys(void) {
}
}
+void run_pubkey_comparison(void) {
+ unsigned char pk1_ser[33] = {
+ 0x02,
+ 0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11,
+ 0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23
+ };
+ const unsigned char pk2_ser[33] = {
+ 0x02,
+ 0xde, 0x36, 0x0e, 0x87, 0x59, 0x8f, 0x3c, 0x01, 0x36, 0x2a, 0x2a, 0xb8, 0xc6, 0xf4, 0x5e, 0x4d,
+ 0xb2, 0xc2, 0xd5, 0x03, 0xa7, 0xf9, 0xf1, 0x4f, 0xa8, 0xfa, 0x95, 0xa8, 0xe9, 0x69, 0x76, 0x1c
+ };
+ secp256k1_pubkey pk1;
+ secp256k1_pubkey pk2;
+ int32_t ecount = 0;
+
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pk1, pk1_ser, sizeof(pk1_ser)) == 1);
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pk2, pk2_ser, sizeof(pk2_ser)) == 1);
+
+ secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, NULL, &pk2) < 0);
+ CHECK(ecount == 1);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk1, NULL) > 0);
+ CHECK(ecount == 2);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk1, &pk2) < 0);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk2, &pk1) > 0);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk1, &pk1) == 0);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk2, &pk2) == 0);
+ CHECK(ecount == 2);
+ {
+ secp256k1_pubkey pk_tmp;
+ memset(&pk_tmp, 0, sizeof(pk_tmp)); /* illegal pubkey */
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk_tmp, &pk2) < 0);
+ CHECK(ecount == 3);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk_tmp, &pk_tmp) == 0);
+ CHECK(ecount == 5);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk2, &pk_tmp) > 0);
+ CHECK(ecount == 6);
+ }
+
+ secp256k1_context_set_illegal_callback(ctx, NULL, NULL);
+
+ /* Make pk2 the same as pk1 but with 3 rather than 2. Note that in
+ * an uncompressed encoding, these would have the opposite ordering */
+ pk1_ser[0] = 3;
+ CHECK(secp256k1_ec_pubkey_parse(ctx, &pk2, pk1_ser, sizeof(pk1_ser)) == 1);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk1, &pk2) < 0);
+ CHECK(secp256k1_ec_pubkey_cmp(ctx, &pk2, &pk1) > 0);
+}
+
void run_random_pubkeys(void) {
int i;
for (i = 0; i < 10*count; i++) {
@@ -6408,7 +6567,7 @@ int main(int argc, char **argv) {
count = strtol(argv[1], NULL, 0);
} else {
const char* env = getenv("SECP256K1_TEST_ITERS");
- if (env) {
+ if (env && strlen(env) > 0) {
count = strtol(env, NULL, 0);
}
}
@@ -6442,6 +6601,7 @@ int main(int argc, char **argv) {
run_sha256_tests();
run_hmac_sha256_tests();
run_rfc6979_hmac_sha256_tests();
+ run_tagged_sha256_tests();
/* scalar tests */
run_scalar_tests();
@@ -6449,6 +6609,7 @@ int main(int argc, char **argv) {
/* field tests */
run_field_misc();
run_field_convert();
+ run_fe_mul();
run_sqr();
run_sqrt();
@@ -6485,6 +6646,7 @@ int main(int argc, char **argv) {
#endif
/* ecdsa tests */
+ run_pubkey_comparison();
run_random_pubkeys();
run_ecdsa_der_parse();
run_ecdsa_sign_verify();
diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c
index 2bb5381446..5b9a3035d9 100644
--- a/src/secp256k1/src/tests_exhaustive.c
+++ b/src/secp256k1/src/tests_exhaustive.c
@@ -10,7 +10,6 @@
#include <stdio.h>
#include <stdlib.h>
-
#include <time.h>
#undef USE_ECMULT_STATIC_PRECOMPUTATION
@@ -20,10 +19,10 @@
#define EXHAUSTIVE_TEST_ORDER 13
#endif
-#include "include/secp256k1.h"
+#include "secp256k1.c"
+#include "../include/secp256k1.h"
#include "assumptions.h"
#include "group.h"
-#include "secp256k1.c"
#include "testrand_impl.h"
static int count = 2;
@@ -303,6 +302,7 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou
if (skip_section(&iter)) continue;
for (k = 1; k < EXHAUSTIVE_TEST_ORDER; k++) { /* nonce */
const int starting_k = k;
+ int ret;
secp256k1_ecdsa_signature sig;
secp256k1_scalar sk, msg, r, s, expected_r;
unsigned char sk32[32], msg32[32];
@@ -311,7 +311,8 @@ void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *grou
secp256k1_scalar_get_b32(sk32, &sk);
secp256k1_scalar_get_b32(msg32, &msg);
- secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
+ ret = secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
+ CHECK(ret == 1);
secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
/* Note that we compute expected_r *after* signing -- this is important
diff --git a/src/secp256k1/src/valgrind_ctime_test.c b/src/secp256k1/src/valgrind_ctime_test.c
index cfca5a196e..ea6d4b3deb 100644
--- a/src/secp256k1/src/valgrind_ctime_test.c
+++ b/src/secp256k1/src/valgrind_ctime_test.c
@@ -7,24 +7,24 @@
#include <valgrind/memcheck.h>
#include <stdio.h>
-#include "include/secp256k1.h"
+#include "../include/secp256k1.h"
#include "assumptions.h"
#include "util.h"
#ifdef ENABLE_MODULE_ECDH
-# include "include/secp256k1_ecdh.h"
+# include "../include/secp256k1_ecdh.h"
#endif
#ifdef ENABLE_MODULE_RECOVERY
-# include "include/secp256k1_recovery.h"
+# include "../include/secp256k1_recovery.h"
#endif
#ifdef ENABLE_MODULE_EXTRAKEYS
-# include "include/secp256k1_extrakeys.h"
+# include "../include/secp256k1_extrakeys.h"
#endif
#ifdef ENABLE_MODULE_SCHNORRSIG
-#include "include/secp256k1_schnorrsig.h"
+#include "../include/secp256k1_schnorrsig.h"
#endif
void run_tests(secp256k1_context *ctx, unsigned char *key);
@@ -166,7 +166,7 @@ void run_tests(secp256k1_context *ctx, unsigned char *key) {
ret = secp256k1_keypair_create(ctx, &keypair, key);
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret == 1);
- ret = secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL, NULL);
+ ret = secp256k1_schnorrsig_sign(ctx, sig, msg, &keypair, NULL);
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret == 1);
#endif
diff --git a/src/test/addrman_tests.cpp b/src/test/addrman_tests.cpp
index 5e5c5eba69..79c7102c4f 100644
--- a/src/test/addrman_tests.cpp
+++ b/src/test/addrman_tests.cpp
@@ -34,6 +34,7 @@ public:
//! Ensure that bucket placement is always the same for testing purposes.
void MakeDeterministic()
{
+ LOCK(cs);
nKey.SetNull();
insecure_rand = FastRandomContext(true);
}
@@ -87,11 +88,11 @@ public:
{
CAddrMan::Clear();
if (deterministic) {
+ LOCK(cs);
nKey.SetNull();
insecure_rand = FastRandomContext(true);
}
}
-
};
static CNetAddr ResolveIP(const std::string& ip)
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 5668ead1fb..0bfe6eecd9 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
const CChainParams& chainparams = Params();
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
- *m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
+ *m_node.chainman, *m_node.mempool, false);
// Mock an outbound peer
CAddress addr1(ip(0xa0b0c001), NODE_NONE);
@@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
const CChainParams& chainparams = Params();
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, nullptr,
- *m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
+ *m_node.chainman, *m_node.mempool, false);
constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS;
CConnman::Options options;
@@ -194,7 +194,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
- *m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
+ *m_node.chainman, *m_node.mempool, false);
CNetAddr tor_netaddr;
BOOST_REQUIRE(
@@ -288,7 +288,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
auto banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
auto connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman);
auto peerLogic = PeerManager::make(chainparams, *connman, *m_node.addrman, banman.get(),
- *m_node.scheduler, *m_node.chainman, *m_node.mempool, false);
+ *m_node.chainman, *m_node.mempool, false);
banman->ClearBanned();
int64_t nStartTime = GetTime();
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 92c34e74d9..4c29a8ee53 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -12,6 +12,7 @@
#include <time.h>
#include <util/asmap.h>
+#include <cassert>
#include <cstdint>
#include <optional>
#include <string>
@@ -25,10 +26,200 @@ void initialize_addrman()
class CAddrManDeterministic : public CAddrMan
{
public:
- void MakeDeterministic(const uint256& random_seed)
+ FuzzedDataProvider& m_fuzzed_data_provider;
+
+ explicit CAddrManDeterministic(FuzzedDataProvider& fuzzed_data_provider)
+ : m_fuzzed_data_provider(fuzzed_data_provider)
+ {
+ WITH_LOCK(cs, insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
+ if (fuzzed_data_provider.ConsumeBool()) {
+ m_asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
+ if (!SanityCheckASMap(m_asmap)) {
+ m_asmap.clear();
+ }
+ }
+ }
+
+ /**
+ * Generate a random address. Always returns a valid address.
+ */
+ CNetAddr RandAddr() EXCLUSIVE_LOCKS_REQUIRED(cs)
+ {
+ CNetAddr addr;
+ if (m_fuzzed_data_provider.remaining_bytes() > 1 && m_fuzzed_data_provider.ConsumeBool()) {
+ addr = ConsumeNetAddr(m_fuzzed_data_provider);
+ } else {
+ // The networks [1..6] correspond to CNetAddr::BIP155Network (private).
+ static const std::map<uint8_t, uint8_t> net_len_map = {{1, ADDR_IPV4_SIZE},
+ {2, ADDR_IPV6_SIZE},
+ {4, ADDR_TORV3_SIZE},
+ {5, ADDR_I2P_SIZE},
+ {6, ADDR_CJDNS_SIZE}};
+ uint8_t net = insecure_rand.randrange(5) + 1; // [1..5]
+ if (net == 3) {
+ net = 6;
+ }
+
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
+
+ s << net;
+ s << insecure_rand.randbytes(net_len_map.at(net));
+
+ s >> addr;
+ }
+
+ // Return a dummy IPv4 5.5.5.5 if we generated an invalid address.
+ if (!addr.IsValid()) {
+ in_addr v4_addr = {};
+ v4_addr.s_addr = 0x05050505;
+ addr = CNetAddr{v4_addr};
+ }
+
+ return addr;
+ }
+
+ /**
+ * Fill this addrman with lots of addresses from lots of sources.
+ */
+ void Fill()
{
- insecure_rand = FastRandomContext{random_seed};
- Clear();
+ LOCK(cs);
+
+ // Add some of the addresses directly to the "tried" table.
+
+ // 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33%
+ const size_t n = m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 3);
+
+ const size_t num_sources = m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(10, 50);
+ CNetAddr prev_source;
+ // Use insecure_rand inside the loops instead of m_fuzzed_data_provider because when
+ // the latter is exhausted it just returns 0.
+ for (size_t i = 0; i < num_sources; ++i) {
+ const auto source = RandAddr();
+ const size_t num_addresses = insecure_rand.randrange(500) + 1; // [1..500]
+
+ for (size_t j = 0; j < num_addresses; ++j) {
+ const auto addr = CAddress{CService{RandAddr(), 8333}, NODE_NETWORK};
+ const auto time_penalty = insecure_rand.randrange(100000001);
+#if 1
+ // 2.83 sec to fill.
+ if (n > 0 && mapInfo.size() % n == 0 && mapAddr.find(addr) == mapAddr.end()) {
+ // Add to the "tried" table (if the bucket slot is free).
+ const CAddrInfo dummy{addr, source};
+ const int bucket = dummy.GetTriedBucket(nKey, m_asmap);
+ const int bucket_pos = dummy.GetBucketPosition(nKey, false, bucket);
+ if (vvTried[bucket][bucket_pos] == -1) {
+ int id;
+ CAddrInfo* addr_info = Create(addr, source, &id);
+ vvTried[bucket][bucket_pos] = id;
+ addr_info->fInTried = true;
+ ++nTried;
+ }
+ } else {
+ // Add to the "new" table.
+ Add_(addr, source, time_penalty);
+ }
+#else
+ // 261.91 sec to fill.
+ Add_(addr, source, time_penalty);
+ if (n > 0 && mapInfo.size() % n == 0) {
+ Good_(addr, false, GetTime());
+ }
+#endif
+ // Add 10% of the addresses from more than one source.
+ if (insecure_rand.randrange(10) == 0 && prev_source.IsValid()) {
+ Add_(addr, prev_source, time_penalty);
+ }
+ }
+ prev_source = source;
+ }
+ }
+
+ /**
+ * Compare with another AddrMan.
+ * This compares:
+ * - the values in `mapInfo` (the keys aka ids are ignored)
+ * - vvNew entries refer to the same addresses
+ * - vvTried entries refer to the same addresses
+ */
+ bool operator==(const CAddrManDeterministic& other)
+ {
+ LOCK2(cs, other.cs);
+
+ if (mapInfo.size() != other.mapInfo.size() || nNew != other.nNew ||
+ nTried != other.nTried) {
+ return false;
+ }
+
+ // Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
+ // Keys may be different.
+
+ using CAddrInfoHasher = std::function<size_t(const CAddrInfo&)>;
+ using CAddrInfoEq = std::function<bool(const CAddrInfo&, const CAddrInfo&)>;
+
+ CNetAddrHash netaddr_hasher;
+
+ CAddrInfoHasher addrinfo_hasher = [&netaddr_hasher](const CAddrInfo& a) {
+ return netaddr_hasher(static_cast<CNetAddr>(a)) ^ netaddr_hasher(a.source) ^
+ a.nLastSuccess ^ a.nAttempts ^ a.nRefCount ^ a.fInTried;
+ };
+
+ CAddrInfoEq addrinfo_eq = [](const CAddrInfo& lhs, const CAddrInfo& rhs) {
+ return static_cast<CNetAddr>(lhs) == static_cast<CNetAddr>(rhs) &&
+ lhs.source == rhs.source && lhs.nLastSuccess == rhs.nLastSuccess &&
+ lhs.nAttempts == rhs.nAttempts && lhs.nRefCount == rhs.nRefCount &&
+ lhs.fInTried == rhs.fInTried;
+ };
+
+ using Addresses = std::unordered_set<CAddrInfo, CAddrInfoHasher, CAddrInfoEq>;
+
+ const size_t num_addresses{mapInfo.size()};
+
+ Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
+ for (const auto& [id, addr] : mapInfo) {
+ addresses.insert(addr);
+ }
+
+ Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
+ for (const auto& [id, addr] : other.mapInfo) {
+ other_addresses.insert(addr);
+ }
+
+ if (addresses != other_addresses) {
+ return false;
+ }
+
+ auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED(cs, other.cs) {
+ if (id == -1 && other_id == -1) {
+ return true;
+ }
+ if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) {
+ return false;
+ }
+ return mapInfo.at(id) == other.mapInfo.at(other_id);
+ };
+
+ // Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
+ // contains just an id and the address is to be found in `mapInfo.at(id)`. The ids
+ // themselves may differ between `vvNew` and `other.vvNew`.
+ for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
+ for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
+ if (!IdsReferToSameAddress(vvNew[i][j], other.vvNew[i][j])) {
+ return false;
+ }
+ }
+ }
+
+ // Same for `vvTried`.
+ for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
+ for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
+ if (!IdsReferToSameAddress(vvTried[i][j], other.vvTried[i][j])) {
+ return false;
+ }
+ }
+ }
+
+ return true;
}
};
@@ -36,14 +227,7 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
- CAddrManDeterministic addr_man;
- addr_man.MakeDeterministic(ConsumeUInt256(fuzzed_data_provider));
- if (fuzzed_data_provider.ConsumeBool()) {
- addr_man.m_asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
- if (!SanityCheckASMap(addr_man.m_asmap)) {
- addr_man.m_asmap.clear();
- }
- }
+ CAddrManDeterministic addr_man{fuzzed_data_provider};
if (fuzzed_data_provider.ConsumeBool()) {
const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
CDataStream ds(serialized_data, SER_DISK, INIT_PROTO_VERSION);
@@ -114,12 +298,30 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
});
}
const CAddrMan& const_addr_man{addr_man};
- (void)/*const_*/addr_man.GetAddr(
+ (void)const_addr_man.GetAddr(
/* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
/* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
/* network */ std::nullopt);
- (void)/*const_*/addr_man.Select(fuzzed_data_provider.ConsumeBool());
+ (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool());
(void)const_addr_man.size();
CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
data_stream << const_addr_man;
}
+
+// Check that serialize followed by unserialize produces the same addrman.
+FUZZ_TARGET_INIT(addrman_serdeser, initialize_addrman)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ SetMockTime(ConsumeTime(fuzzed_data_provider));
+
+ CAddrManDeterministic addr_man1{fuzzed_data_provider};
+ CAddrManDeterministic addr_man2{fuzzed_data_provider};
+ addr_man2.m_asmap = addr_man1.m_asmap;
+
+ CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
+
+ addr_man1.Fill();
+ data_stream << addr_man1;
+ data_stream >> addr_man2;
+ assert(addr_man1 == addr_man2);
+}
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index 1986b5e4c8..46a9f623ac 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -52,8 +52,7 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
const bool start_with_corrupted_banlist{fuzzed_data_provider.ConsumeBool()};
bool force_read_and_write_to_err{false};
if (start_with_corrupted_banlist) {
- const std::string sfx{fuzzed_data_provider.ConsumeBool() ? ".dat" : ".json"};
- assert(WriteBinaryFile(banlist_file.string() + sfx,
+ assert(WriteBinaryFile(banlist_file.string() + ".json",
fuzzed_data_provider.ConsumeRandomLengthString()));
} else {
force_read_and_write_to_err = fuzzed_data_provider.ConsumeBool();
@@ -109,11 +108,8 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
BanMan ban_man_read{banlist_file, /* client_interface */ nullptr, /* default_ban_time */ 0};
banmap_t banmap_read;
ban_man_read.GetBanned(banmap_read);
- // Assert temporarily disabled to allow the remainder of the fuzz test to run while a
- // fix is being worked on. See https://github.com/bitcoin/bitcoin/pull/22517
- (void)(banmap == banmap_read);
+ assert(banmap == banmap_read);
}
}
- fs::remove(banlist_file.string() + ".dat");
fs::remove(banlist_file.string() + ".json");
}
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index 721e4360d0..d5b56cb7cd 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -100,228 +100,217 @@ void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT
} // namespace
FUZZ_TARGET_DESERIALIZE(block_filter_deserialize, {
- BlockFilter block_filter;
- DeserializeFromFuzzingInput(buffer, block_filter);
+ BlockFilter block_filter;
+ DeserializeFromFuzzingInput(buffer, block_filter);
})
FUZZ_TARGET_DESERIALIZE(addr_info_deserialize, {
- CAddrInfo addr_info;
- DeserializeFromFuzzingInput(buffer, addr_info);
+ CAddrInfo addr_info;
+ DeserializeFromFuzzingInput(buffer, addr_info);
})
FUZZ_TARGET_DESERIALIZE(block_file_info_deserialize, {
- CBlockFileInfo block_file_info;
- DeserializeFromFuzzingInput(buffer, block_file_info);
+ CBlockFileInfo block_file_info;
+ DeserializeFromFuzzingInput(buffer, block_file_info);
})
FUZZ_TARGET_DESERIALIZE(block_header_and_short_txids_deserialize, {
- CBlockHeaderAndShortTxIDs block_header_and_short_txids;
- DeserializeFromFuzzingInput(buffer, block_header_and_short_txids);
+ CBlockHeaderAndShortTxIDs block_header_and_short_txids;
+ DeserializeFromFuzzingInput(buffer, block_header_and_short_txids);
})
FUZZ_TARGET_DESERIALIZE(fee_rate_deserialize, {
- CFeeRate fee_rate;
- DeserializeFromFuzzingInput(buffer, fee_rate);
- AssertEqualAfterSerializeDeserialize(fee_rate);
+ CFeeRate fee_rate;
+ DeserializeFromFuzzingInput(buffer, fee_rate);
+ AssertEqualAfterSerializeDeserialize(fee_rate);
})
FUZZ_TARGET_DESERIALIZE(merkle_block_deserialize, {
- CMerkleBlock merkle_block;
- DeserializeFromFuzzingInput(buffer, merkle_block);
+ CMerkleBlock merkle_block;
+ DeserializeFromFuzzingInput(buffer, merkle_block);
})
FUZZ_TARGET_DESERIALIZE(out_point_deserialize, {
- COutPoint out_point;
- DeserializeFromFuzzingInput(buffer, out_point);
- AssertEqualAfterSerializeDeserialize(out_point);
+ COutPoint out_point;
+ DeserializeFromFuzzingInput(buffer, out_point);
+ AssertEqualAfterSerializeDeserialize(out_point);
})
FUZZ_TARGET_DESERIALIZE(partial_merkle_tree_deserialize, {
- CPartialMerkleTree partial_merkle_tree;
- DeserializeFromFuzzingInput(buffer, partial_merkle_tree);
+ CPartialMerkleTree partial_merkle_tree;
+ DeserializeFromFuzzingInput(buffer, partial_merkle_tree);
})
FUZZ_TARGET_DESERIALIZE(pub_key_deserialize, {
- CPubKey pub_key;
- DeserializeFromFuzzingInput(buffer, pub_key);
- AssertEqualAfterSerializeDeserialize(pub_key);
+ CPubKey pub_key;
+ DeserializeFromFuzzingInput(buffer, pub_key);
+ AssertEqualAfterSerializeDeserialize(pub_key);
})
FUZZ_TARGET_DESERIALIZE(script_deserialize, {
- CScript script;
- DeserializeFromFuzzingInput(buffer, script);
-})
-FUZZ_TARGET_DESERIALIZE(sub_net_deserialize, {
- CSubNet sub_net_1;
- DeserializeFromFuzzingInput(buffer, sub_net_1, INIT_PROTO_VERSION);
- AssertEqualAfterSerializeDeserialize(sub_net_1, INIT_PROTO_VERSION);
- CSubNet sub_net_2;
- DeserializeFromFuzzingInput(buffer, sub_net_2, INIT_PROTO_VERSION | ADDRV2_FORMAT);
- AssertEqualAfterSerializeDeserialize(sub_net_2, INIT_PROTO_VERSION | ADDRV2_FORMAT);
- CSubNet sub_net_3;
- DeserializeFromFuzzingInput(buffer, sub_net_3);
- AssertEqualAfterSerializeDeserialize(sub_net_3, INIT_PROTO_VERSION | ADDRV2_FORMAT);
+ CScript script;
+ DeserializeFromFuzzingInput(buffer, script);
})
FUZZ_TARGET_DESERIALIZE(tx_in_deserialize, {
- CTxIn tx_in;
- DeserializeFromFuzzingInput(buffer, tx_in);
- AssertEqualAfterSerializeDeserialize(tx_in);
+ CTxIn tx_in;
+ DeserializeFromFuzzingInput(buffer, tx_in);
+ AssertEqualAfterSerializeDeserialize(tx_in);
})
FUZZ_TARGET_DESERIALIZE(flat_file_pos_deserialize, {
- FlatFilePos flat_file_pos;
- DeserializeFromFuzzingInput(buffer, flat_file_pos);
- AssertEqualAfterSerializeDeserialize(flat_file_pos);
+ FlatFilePos flat_file_pos;
+ DeserializeFromFuzzingInput(buffer, flat_file_pos);
+ AssertEqualAfterSerializeDeserialize(flat_file_pos);
})
FUZZ_TARGET_DESERIALIZE(key_origin_info_deserialize, {
- KeyOriginInfo key_origin_info;
- DeserializeFromFuzzingInput(buffer, key_origin_info);
- AssertEqualAfterSerializeDeserialize(key_origin_info);
+ KeyOriginInfo key_origin_info;
+ DeserializeFromFuzzingInput(buffer, key_origin_info);
+ AssertEqualAfterSerializeDeserialize(key_origin_info);
})
FUZZ_TARGET_DESERIALIZE(partially_signed_transaction_deserialize, {
- PartiallySignedTransaction partially_signed_transaction;
- DeserializeFromFuzzingInput(buffer, partially_signed_transaction);
+ PartiallySignedTransaction partially_signed_transaction;
+ DeserializeFromFuzzingInput(buffer, partially_signed_transaction);
})
FUZZ_TARGET_DESERIALIZE(prefilled_transaction_deserialize, {
- PrefilledTransaction prefilled_transaction;
- DeserializeFromFuzzingInput(buffer, prefilled_transaction);
+ PrefilledTransaction prefilled_transaction;
+ DeserializeFromFuzzingInput(buffer, prefilled_transaction);
})
FUZZ_TARGET_DESERIALIZE(psbt_input_deserialize, {
- PSBTInput psbt_input;
- DeserializeFromFuzzingInput(buffer, psbt_input);
+ PSBTInput psbt_input;
+ DeserializeFromFuzzingInput(buffer, psbt_input);
})
FUZZ_TARGET_DESERIALIZE(psbt_output_deserialize, {
- PSBTOutput psbt_output;
- DeserializeFromFuzzingInput(buffer, psbt_output);
+ PSBTOutput psbt_output;
+ DeserializeFromFuzzingInput(buffer, psbt_output);
})
FUZZ_TARGET_DESERIALIZE(block_deserialize, {
- CBlock block;
- DeserializeFromFuzzingInput(buffer, block);
+ CBlock block;
+ DeserializeFromFuzzingInput(buffer, block);
})
FUZZ_TARGET_DESERIALIZE(blocklocator_deserialize, {
- CBlockLocator bl;
- DeserializeFromFuzzingInput(buffer, bl);
+ CBlockLocator bl;
+ DeserializeFromFuzzingInput(buffer, bl);
})
FUZZ_TARGET_DESERIALIZE(blockmerkleroot, {
- CBlock block;
- DeserializeFromFuzzingInput(buffer, block);
- bool mutated;
- BlockMerkleRoot(block, &mutated);
+ CBlock block;
+ DeserializeFromFuzzingInput(buffer, block);
+ bool mutated;
+ BlockMerkleRoot(block, &mutated);
})
FUZZ_TARGET_DESERIALIZE(addrman_deserialize, {
- CAddrMan am;
- DeserializeFromFuzzingInput(buffer, am);
+ CAddrMan am;
+ DeserializeFromFuzzingInput(buffer, am);
})
FUZZ_TARGET_DESERIALIZE(blockheader_deserialize, {
- CBlockHeader bh;
- DeserializeFromFuzzingInput(buffer, bh);
+ CBlockHeader bh;
+ DeserializeFromFuzzingInput(buffer, bh);
})
FUZZ_TARGET_DESERIALIZE(banentry_deserialize, {
- CBanEntry be;
- DeserializeFromFuzzingInput(buffer, be);
+ CBanEntry be;
+ DeserializeFromFuzzingInput(buffer, be);
})
FUZZ_TARGET_DESERIALIZE(txundo_deserialize, {
- CTxUndo tu;
- DeserializeFromFuzzingInput(buffer, tu);
+ CTxUndo tu;
+ DeserializeFromFuzzingInput(buffer, tu);
})
FUZZ_TARGET_DESERIALIZE(blockundo_deserialize, {
- CBlockUndo bu;
- DeserializeFromFuzzingInput(buffer, bu);
+ CBlockUndo bu;
+ DeserializeFromFuzzingInput(buffer, bu);
})
FUZZ_TARGET_DESERIALIZE(coins_deserialize, {
- Coin coin;
- DeserializeFromFuzzingInput(buffer, coin);
+ Coin coin;
+ DeserializeFromFuzzingInput(buffer, coin);
})
FUZZ_TARGET_DESERIALIZE(netaddr_deserialize, {
- CNetAddr na;
- DeserializeFromFuzzingInput(buffer, na);
- if (na.IsAddrV1Compatible()) {
- AssertEqualAfterSerializeDeserialize(na);
- }
- AssertEqualAfterSerializeDeserialize(na, INIT_PROTO_VERSION | ADDRV2_FORMAT);
+ CNetAddr na;
+ DeserializeFromFuzzingInput(buffer, na);
+ if (na.IsAddrV1Compatible()) {
+ AssertEqualAfterSerializeDeserialize(na);
+ }
+ AssertEqualAfterSerializeDeserialize(na, INIT_PROTO_VERSION | ADDRV2_FORMAT);
})
FUZZ_TARGET_DESERIALIZE(service_deserialize, {
- CService s;
- DeserializeFromFuzzingInput(buffer, s);
- if (s.IsAddrV1Compatible()) {
- AssertEqualAfterSerializeDeserialize(s);
- }
- AssertEqualAfterSerializeDeserialize(s, INIT_PROTO_VERSION | ADDRV2_FORMAT);
- CService s1;
- DeserializeFromFuzzingInput(buffer, s1, INIT_PROTO_VERSION);
- AssertEqualAfterSerializeDeserialize(s1, INIT_PROTO_VERSION);
- assert(s1.IsAddrV1Compatible());
- CService s2;
- DeserializeFromFuzzingInput(buffer, s2, INIT_PROTO_VERSION | ADDRV2_FORMAT);
- AssertEqualAfterSerializeDeserialize(s2, INIT_PROTO_VERSION | ADDRV2_FORMAT);
+ CService s;
+ DeserializeFromFuzzingInput(buffer, s);
+ if (s.IsAddrV1Compatible()) {
+ AssertEqualAfterSerializeDeserialize(s);
+ }
+ AssertEqualAfterSerializeDeserialize(s, INIT_PROTO_VERSION | ADDRV2_FORMAT);
+ CService s1;
+ DeserializeFromFuzzingInput(buffer, s1, INIT_PROTO_VERSION);
+ AssertEqualAfterSerializeDeserialize(s1, INIT_PROTO_VERSION);
+ assert(s1.IsAddrV1Compatible());
+ CService s2;
+ DeserializeFromFuzzingInput(buffer, s2, INIT_PROTO_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(s2, INIT_PROTO_VERSION | ADDRV2_FORMAT);
})
FUZZ_TARGET_DESERIALIZE(messageheader_deserialize, {
- CMessageHeader mh;
- DeserializeFromFuzzingInput(buffer, mh);
- (void)mh.IsCommandValid();
+ CMessageHeader mh;
+ DeserializeFromFuzzingInput(buffer, mh);
+ (void)mh.IsCommandValid();
})
FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_notime, {
- CAddress a;
- DeserializeFromFuzzingInput(buffer, a, INIT_PROTO_VERSION);
- // A CAddress without nTime (as is expected under INIT_PROTO_VERSION) will roundtrip
- // in all 5 formats (with/without nTime, v1/v2, network/disk)
- AssertEqualAfterSerializeDeserialize(a, INIT_PROTO_VERSION);
- AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
- AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
- AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
- AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
+ CAddress a;
+ DeserializeFromFuzzingInput(buffer, a, INIT_PROTO_VERSION);
+ // A CAddress without nTime (as is expected under INIT_PROTO_VERSION) will roundtrip
+ // in all 5 formats (with/without nTime, v1/v2, network/disk)
+ AssertEqualAfterSerializeDeserialize(a, INIT_PROTO_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
})
FUZZ_TARGET_DESERIALIZE(address_deserialize_v1_withtime, {
- CAddress a;
- DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION);
- // A CAddress in V1 mode will roundtrip in all 4 formats that have nTime.
- AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
- AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
- AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
- AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
+ CAddress a;
+ DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION);
+ // A CAddress in V1 mode will roundtrip in all 4 formats that have nTime.
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
})
FUZZ_TARGET_DESERIALIZE(address_deserialize_v2, {
- CAddress a;
- DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION | ADDRV2_FORMAT);
- // A CAddress in V2 mode will roundtrip in both V2 formats, and also in the V1 formats
- // with time if it's V1 compatible.
- if (a.IsAddrV1Compatible()) {
- AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
- AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
- }
- AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
- AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
+ CAddress a;
+ DeserializeFromFuzzingInput(buffer, a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ // A CAddress in V2 mode will roundtrip in both V2 formats, and also in the V1 formats
+ // with time if it's V1 compatible.
+ if (a.IsAddrV1Compatible()) {
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION);
+ AssertEqualAfterSerializeDeserialize(a, 0, SER_DISK);
+ }
+ AssertEqualAfterSerializeDeserialize(a, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ AssertEqualAfterSerializeDeserialize(a, ADDRV2_FORMAT, SER_DISK);
})
FUZZ_TARGET_DESERIALIZE(inv_deserialize, {
- CInv i;
- DeserializeFromFuzzingInput(buffer, i);
+ CInv i;
+ DeserializeFromFuzzingInput(buffer, i);
})
FUZZ_TARGET_DESERIALIZE(bloomfilter_deserialize, {
- CBloomFilter bf;
- DeserializeFromFuzzingInput(buffer, bf);
+ CBloomFilter bf;
+ DeserializeFromFuzzingInput(buffer, bf);
})
FUZZ_TARGET_DESERIALIZE(diskblockindex_deserialize, {
- CDiskBlockIndex dbi;
- DeserializeFromFuzzingInput(buffer, dbi);
+ CDiskBlockIndex dbi;
+ DeserializeFromFuzzingInput(buffer, dbi);
})
FUZZ_TARGET_DESERIALIZE(txoutcompressor_deserialize, {
- CTxOut to;
- auto toc = Using<TxOutCompression>(to);
- DeserializeFromFuzzingInput(buffer, toc);
+ CTxOut to;
+ auto toc = Using<TxOutCompression>(to);
+ DeserializeFromFuzzingInput(buffer, toc);
})
FUZZ_TARGET_DESERIALIZE(blocktransactions_deserialize, {
- BlockTransactions bt;
- DeserializeFromFuzzingInput(buffer, bt);
+ BlockTransactions bt;
+ DeserializeFromFuzzingInput(buffer, bt);
})
FUZZ_TARGET_DESERIALIZE(blocktransactionsrequest_deserialize, {
- BlockTransactionsRequest btr;
- DeserializeFromFuzzingInput(buffer, btr);
+ BlockTransactionsRequest btr;
+ DeserializeFromFuzzingInput(buffer, btr);
})
FUZZ_TARGET_DESERIALIZE(snapshotmetadata_deserialize, {
- SnapshotMetadata snapshot_metadata;
- DeserializeFromFuzzingInput(buffer, snapshot_metadata);
+ SnapshotMetadata snapshot_metadata;
+ DeserializeFromFuzzingInput(buffer, snapshot_metadata);
})
FUZZ_TARGET_DESERIALIZE(uint160_deserialize, {
- uint160 u160;
- DeserializeFromFuzzingInput(buffer, u160);
- AssertEqualAfterSerializeDeserialize(u160);
+ uint160 u160;
+ DeserializeFromFuzzingInput(buffer, u160);
+ AssertEqualAfterSerializeDeserialize(u160);
})
FUZZ_TARGET_DESERIALIZE(uint256_deserialize, {
- uint256 u256;
- DeserializeFromFuzzingInput(buffer, u256);
- AssertEqualAfterSerializeDeserialize(u256);
-})
- // Classes intentionally not covered in this file since their deserialization code is
- // fuzzed elsewhere:
- // * Deserialization of CTxOut is fuzzed in test/fuzz/tx_out.cpp
- // * Deserialization of CMutableTransaction is fuzzed in src/test/fuzz/transaction.cpp
+ uint256 u256;
+ DeserializeFromFuzzingInput(buffer, u256);
+ AssertEqualAfterSerializeDeserialize(u256);
+})
+// Classes intentionally not covered in this file since their deserialization code is
+// fuzzed elsewhere:
+// * Deserialization of CTxOut is fuzzed in test/fuzz/tx_out.cpp
+// * Deserialization of CMutableTransaction is fuzzed in src/test/fuzz/transaction.cpp
diff --git a/src/test/fuzz/kitchen_sink.cpp b/src/test/fuzz/kitchen_sink.cpp
index 908e9a1c83..82f3a306c5 100644
--- a/src/test/fuzz/kitchen_sink.cpp
+++ b/src/test/fuzz/kitchen_sink.cpp
@@ -13,6 +13,7 @@
#include <array>
#include <cstdint>
+#include <optional>
#include <vector>
namespace {
@@ -46,11 +47,10 @@ FUZZ_TARGET(kitchen_sink)
const OutputType output_type = fuzzed_data_provider.PickValueInArray(OUTPUT_TYPES);
const std::string& output_type_string = FormatOutputType(output_type);
- OutputType output_type_parsed;
- const bool parsed = ParseOutputType(output_type_string, output_type_parsed);
+ const std::optional<OutputType> parsed = ParseOutputType(output_type_string);
assert(parsed);
- assert(output_type == output_type_parsed);
- (void)ParseOutputType(fuzzed_data_provider.ConsumeRandomLengthString(64), output_type_parsed);
+ assert(output_type == parsed.value());
+ (void)ParseOutputType(fuzzed_data_provider.ConsumeRandomLengthString(64));
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
const std::vector<bool> bits = BytesToBits(bytes);
diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp
index fe850a6959..684324c36e 100644
--- a/src/test/fuzz/script_sign.cpp
+++ b/src/test/fuzz/script_sign.cpp
@@ -13,6 +13,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <util/translation.h>
#include <cassert>
#include <cstdint>
@@ -135,7 +136,7 @@ FUZZ_TARGET_INIT(script_sign, initialize_script_sign)
}
coins[*outpoint] = *coin;
}
- std::map<int, std::string> input_errors;
+ std::map<int, bilingual_str> input_errors;
(void)SignTransaction(sign_transaction_tx_to, &provider, coins, fuzzed_data_provider.ConsumeIntegral<int>(), input_errors);
}
}
diff --git a/src/test/fuzz/string.cpp b/src/test/fuzz/string.cpp
index 286375f7ae..0c1b45b86c 100644
--- a/src/test/fuzz/string.cpp
+++ b/src/test/fuzz/string.cpp
@@ -66,8 +66,7 @@ FUZZ_TARGET(string)
(void)ParseNonRFCJSONValue(random_string_1);
} catch (const std::runtime_error&) {
}
- OutputType output_type;
- (void)ParseOutputType(random_string_1, output_type);
+ (void)ParseOutputType(random_string_1);
(void)RemovePrefix(random_string_1, random_string_2);
(void)ResolveErrMsg(random_string_1, random_string_2);
try {
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index e20c5e4e8f..7f44dcf20e 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Copyright (c) 2011-2021 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -16,6 +16,7 @@
#include <util/system.h>
#include <util/time.h>
#include <validation.h>
+#include <versionbits.h>
#include <test/util/setup_common.h>
@@ -51,36 +52,25 @@ BlockAssembler MinerTestingSetup::AssemblerForTest(const CChainParams& params)
constexpr static struct {
unsigned char extranonce;
unsigned int nonce;
-} blockinfo[] = {
- {4, 0xa4a3e223}, {2, 0x15c32f9e}, {1, 0x0375b547}, {1, 0x7004a8a5},
- {2, 0xce440296}, {2, 0x52cfe198}, {1, 0x77a72cd0}, {2, 0xbb5d6f84},
- {2, 0x83f30c2c}, {1, 0x48a73d5b}, {1, 0xef7dcd01}, {2, 0x6809c6c4},
- {2, 0x0883ab3c}, {1, 0x087bbbe2}, {2, 0x2104a814}, {2, 0xdffb6daa},
- {1, 0xee8a0a08}, {2, 0xba4237c1}, {1, 0xa70349dc}, {1, 0x344722bb},
- {3, 0xd6294733}, {2, 0xec9f5c94}, {2, 0xca2fbc28}, {1, 0x6ba4f406},
- {2, 0x015d4532}, {1, 0x6e119b7c}, {2, 0x43e8f314}, {2, 0x27962f38},
- {2, 0xb571b51b}, {2, 0xb36bee23}, {2, 0xd17924a8}, {2, 0x6bc212d9},
- {1, 0x630d4948}, {2, 0x9a4c4ebb}, {2, 0x554be537}, {1, 0xd63ddfc7},
- {2, 0xa10acc11}, {1, 0x759a8363}, {2, 0xfb73090d}, {1, 0xe82c6a34},
- {1, 0xe33e92d7}, {3, 0x658ef5cb}, {2, 0xba32ff22}, {5, 0x0227a10c},
- {1, 0xa9a70155}, {5, 0xd096d809}, {1, 0x37176174}, {1, 0x830b8d0f},
- {1, 0xc6e3910e}, {2, 0x823f3ca8}, {1, 0x99850849}, {1, 0x7521fb81},
- {1, 0xaacaabab}, {1, 0xd645a2eb}, {5, 0x7aea1781}, {5, 0x9d6e4b78},
- {1, 0x4ce90fd8}, {1, 0xabdc832d}, {6, 0x4a34f32a}, {2, 0xf2524c1c},
- {2, 0x1bbeb08a}, {1, 0xad47f480}, {1, 0x9f026aeb}, {1, 0x15a95049},
- {2, 0xd1cb95b2}, {2, 0xf84bbda5}, {1, 0x0fa62cd1}, {1, 0xe05f9169},
- {1, 0x78d194a9}, {5, 0x3e38147b}, {5, 0x737ba0d4}, {1, 0x63378e10},
- {1, 0x6d5f91cf}, {2, 0x88612eb8}, {2, 0xe9639484}, {1, 0xb7fabc9d},
- {2, 0x19b01592}, {1, 0x5a90dd31}, {2, 0x5bd7e028}, {2, 0x94d00323},
- {1, 0xa9b9c01a}, {1, 0x3a40de61}, {1, 0x56e7eec7}, {5, 0x859f7ef6},
- {1, 0xfd8e5630}, {1, 0x2b0c9f7f}, {1, 0xba700e26}, {1, 0x7170a408},
- {1, 0x70de86a8}, {1, 0x74d64cd5}, {1, 0x49e738a1}, {2, 0x6910b602},
- {0, 0x643c565f}, {1, 0x54264b3f}, {2, 0x97ea6396}, {2, 0x55174459},
- {2, 0x03e8779a}, {1, 0x98f34d8f}, {1, 0xc07b2b07}, {1, 0xdfe29668},
- {1, 0x3141c7c1}, {1, 0xb3b595f4}, {1, 0x735abf08}, {5, 0x623bfbce},
- {2, 0xd351e722}, {1, 0xf4ca48c9}, {1, 0x5b19c670}, {1, 0xa164bf0e},
- {2, 0xbbbeb305}, {2, 0xfe1c810a},
-};
+} BLOCKINFO[]{{8, 582909131}, {0, 971462344}, {2, 1169481553}, {6, 66147495}, {7, 427785981}, {8, 80538907},
+ {8, 207348013}, {2, 1951240923}, {4, 215054351}, {1, 491520534}, {8, 1282281282}, {4, 639565734},
+ {3, 248274685}, {8, 1160085976}, {6, 396349768}, {5, 393780549}, {5, 1096899528}, {4, 965381630},
+ {0, 728758712}, {5, 318638310}, {3, 164591898}, {2, 274234550}, {2, 254411237}, {7, 561761812},
+ {2, 268342573}, {0, 402816691}, {1, 221006382}, {6, 538872455}, {7, 393315655}, {4, 814555937},
+ {7, 504879194}, {6, 467769648}, {3, 925972193}, {2, 200581872}, {3, 168915404}, {8, 430446262},
+ {5, 773507406}, {3, 1195366164}, {0, 433361157}, {3, 297051771}, {0, 558856551}, {2, 501614039},
+ {3, 528488272}, {2, 473587734}, {8, 230125274}, {2, 494084400}, {4, 357314010}, {8, 60361686},
+ {7, 640624687}, {3, 480441695}, {8, 1424447925}, {4, 752745419}, {1, 288532283}, {6, 669170574},
+ {5, 1900907591}, {3, 555326037}, {3, 1121014051}, {0, 545835650}, {8, 189196651}, {5, 252371575},
+ {0, 199163095}, {6, 558895874}, {6, 1656839784}, {6, 815175452}, {6, 718677851}, {5, 544000334},
+ {0, 340113484}, {6, 850744437}, {4, 496721063}, {8, 524715182}, {6, 574361898}, {6, 1642305743},
+ {6, 355110149}, {5, 1647379658}, {8, 1103005356}, {7, 556460625}, {3, 1139533992}, {5, 304736030},
+ {2, 361539446}, {2, 143720360}, {6, 201939025}, {7, 423141476}, {4, 574633709}, {3, 1412254823},
+ {4, 873254135}, {0, 341817335}, {6, 53501687}, {3, 179755410}, {5, 172209688}, {8, 516810279},
+ {4, 1228391489}, {8, 325372589}, {6, 550367589}, {0, 876291812}, {7, 412454120}, {7, 717202854},
+ {2, 222677843}, {6, 251778867}, {7, 842004420}, {7, 194762829}, {4, 96668841}, {1, 925485796},
+ {0, 792342903}, {6, 678455063}, {6, 773251385}, {5, 186617471}, {6, 883189502}, {7, 396077336},
+ {8, 254702874}, {0, 455592851}};
static CBlockIndex CreateBlockIndex(int nHeight, CBlockIndex* active_chain_tip) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
@@ -220,20 +210,18 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// We can't make transactions until we have inputs
// Therefore, load 110 blocks :)
- static_assert(std::size(blockinfo) == 110, "Should have 110 blocks to import");
+ static_assert(std::size(BLOCKINFO) == 110, "Should have 110 blocks to import");
int baseheight = 0;
std::vector<CTransactionRef> txFirst;
- for (const auto& bi : blockinfo) {
+ for (const auto& bi : BLOCKINFO) {
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
{
LOCK(cs_main);
- pblock->nVersion = 1;
+ pblock->nVersion = VERSIONBITS_TOP_BITS;
pblock->nTime = m_node.chainman->ActiveChain().Tip()->GetMedianTimePast()+1;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
- txCoinbase.vin[0].scriptSig = CScript();
- txCoinbase.vin[0].scriptSig.push_back(bi.extranonce);
- txCoinbase.vin[0].scriptSig.push_back(m_node.chainman->ActiveChain().Height());
+ txCoinbase.vin[0].scriptSig = CScript{} << (m_node.chainman->ActiveChain().Height() + 1) << bi.extranonce;
txCoinbase.vout.resize(1); // Ignore the (optional) segwit commitment added by CreateNewBlock (as the hardcoded nonces don't account for this)
txCoinbase.vout[0].scriptPubKey = CScript();
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 46f88c1282..acbbf357d2 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -37,6 +37,7 @@ public:
//! Ensure that bucket placement is always the same for testing purposes.
void MakeDeterministic()
{
+ LOCK(cs);
nKey.SetNull();
insecure_rand = FastRandomContext(true);
}
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 5334c4623e..ce81fc378f 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -197,7 +197,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = std::make_unique<CConnman>(0x1337, 0x1337, *m_node.addrman); // Deterministic randomness for tests.
m_node.peerman = PeerManager::make(chainparams, *m_node.connman, *m_node.addrman,
- m_node.banman.get(), *m_node.scheduler, *m_node.chainman,
+ m_node.banman.get(), *m_node.chainman,
*m_node.mempool, false);
{
CConnman::Options options;
@@ -292,7 +292,7 @@ CMutableTransaction TestChain100Setup::CreateValidMempoolTransaction(CTransactio
input_coins.insert({outpoint_to_spend, utxo_to_spend});
// - Default signature hashing type
int nHashType = SIGHASH_ALL;
- std::map<int, std::string> input_errors;
+ std::map<int, bilingual_str> input_errors;
assert(SignTransaction(mempool_txn, &keystore, input_coins, nHashType, input_errors));
// If submit=true, add transaction to the mempool.
diff --git a/src/test/util/wallet.cpp b/src/test/util/wallet.cpp
index fd6012e9fe..061659818f 100644
--- a/src/test/util/wallet.cpp
+++ b/src/test/util/wallet.cpp
@@ -8,6 +8,7 @@
#include <outputtype.h>
#include <script/standard.h>
#ifdef ENABLE_WALLET
+#include <util/translation.h>
#include <wallet/wallet.h>
#endif
@@ -18,7 +19,7 @@ std::string getnewaddress(CWallet& w)
{
constexpr auto output_type = OutputType::BECH32;
CTxDestination dest;
- std::string error;
+ bilingual_str error;
if (!w.GetNewDestination(output_type, "", dest, error)) assert(false);
return EncodeDestination(dest);
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index e0bc10d660..8f4ff6815b 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -77,6 +77,8 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
txCoinbase.vout[1].nValue = txCoinbase.vout[0].nValue;
txCoinbase.vout[0].nValue = 0;
txCoinbase.vin[0].scriptWitness.SetNull();
+ // Always pad with OP_0 at the end to avoid bad-cb-length error
+ txCoinbase.vin[0].scriptSig = CScript{} << WITH_LOCK(::cs_main, return m_node.chainman->m_blockman.LookupBlockIndex(prev_hash)->nHeight + 1) << OP_0;
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
return pblock;
@@ -84,8 +86,8 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock> pblock)
{
- LOCK(cs_main); // For m_node.chainman->m_blockman.LookupBlockIndex
- GenerateCoinbaseCommitment(*pblock, m_node.chainman->m_blockman.LookupBlockIndex(pblock->hashPrevBlock), Params().GetConsensus());
+ const CBlockIndex* prev_block{WITH_LOCK(::cs_main, return m_node.chainman->m_blockman.LookupBlockIndex(pblock->hashPrevBlock))};
+ GenerateCoinbaseCommitment(*pblock, prev_block, Params().GetConsensus());
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
@@ -93,6 +95,11 @@ std::shared_ptr<CBlock> MinerTestingSetup::FinalizeBlock(std::shared_ptr<CBlock>
++(pblock->nNonce);
}
+ // submit block header, so that miner can get the block height from the
+ // global state and the node has the topology of the chain
+ BlockValidationState ignored;
+ BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders({pblock->GetBlockHeader()}, ignored, Params()));
+
return pblock;
}
@@ -147,13 +154,6 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
}
bool ignored;
- BlockValidationState state;
- std::vector<CBlockHeader> headers;
- std::transform(blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr<const CBlock> b) { return b->GetBlockHeader(); });
-
- // Process all the headers so we understand the toplogy of the chain
- BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlockHeaders(headers, state, Params()));
-
// Connect the genesis block and drain any outstanding events
BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored));
SyncWithValidationInterfaceQueue();
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index c5a4bbf1b0..d5a888ac67 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -151,33 +151,17 @@ void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256> &vHashes
}
}
-bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntries &setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string &errString, bool fSearchForParents /* = true */) const
+bool CTxMemPool::CalculateAncestorsAndCheckLimits(size_t entry_size,
+ size_t entry_count,
+ setEntries& setAncestors,
+ CTxMemPoolEntry::Parents& staged_ancestors,
+ uint64_t limitAncestorCount,
+ uint64_t limitAncestorSize,
+ uint64_t limitDescendantCount,
+ uint64_t limitDescendantSize,
+ std::string &errString) const
{
- CTxMemPoolEntry::Parents staged_ancestors;
- const CTransaction &tx = entry.GetTx();
-
- if (fSearchForParents) {
- // Get parents of this transaction that are in the mempool
- // GetMemPoolParents() is only valid for entries in the mempool, so we
- // iterate mapTx to find parents.
- for (unsigned int i = 0; i < tx.vin.size(); i++) {
- std::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
- if (piter) {
- staged_ancestors.insert(**piter);
- if (staged_ancestors.size() + 1 > limitAncestorCount) {
- errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
- return false;
- }
- }
- }
- } else {
- // If we're not searching for parents, we require this to be an
- // entry in the mempool already.
- txiter it = mapTx.iterator_to(entry);
- staged_ancestors = it->GetMemPoolParentsConst();
- }
-
- size_t totalSizeWithAncestors = entry.GetTxSize();
+ size_t totalSizeWithAncestors = entry_size;
while (!staged_ancestors.empty()) {
const CTxMemPoolEntry& stage = staged_ancestors.begin()->get();
@@ -187,10 +171,10 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
staged_ancestors.erase(stage);
totalSizeWithAncestors += stageit->GetTxSize();
- if (stageit->GetSizeWithDescendants() + entry.GetTxSize() > limitDescendantSize) {
+ if (stageit->GetSizeWithDescendants() + entry_size > limitDescendantSize) {
errString = strprintf("exceeds descendant size limit for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limitDescendantSize);
return false;
- } else if (stageit->GetCountWithDescendants() + 1 > limitDescendantCount) {
+ } else if (stageit->GetCountWithDescendants() + entry_count > limitDescendantCount) {
errString = strprintf("too many descendants for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limitDescendantCount);
return false;
} else if (totalSizeWithAncestors > limitAncestorSize) {
@@ -206,7 +190,7 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
if (setAncestors.count(parent_it) == 0) {
staged_ancestors.insert(parent);
}
- if (staged_ancestors.size() + setAncestors.size() + 1 > limitAncestorCount) {
+ if (staged_ancestors.size() + setAncestors.size() + entry_count > limitAncestorCount) {
errString = strprintf("too many unconfirmed ancestors [limit: %u]", limitAncestorCount);
return false;
}
@@ -216,6 +200,80 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr
return true;
}
+bool CTxMemPool::CheckPackageLimits(const Package& package,
+ uint64_t limitAncestorCount,
+ uint64_t limitAncestorSize,
+ uint64_t limitDescendantCount,
+ uint64_t limitDescendantSize,
+ std::string &errString) const
+{
+ CTxMemPoolEntry::Parents staged_ancestors;
+ size_t total_size = 0;
+ for (const auto& tx : package) {
+ total_size += GetVirtualTransactionSize(*tx);
+ for (const auto& input : tx->vin) {
+ std::optional<txiter> piter = GetIter(input.prevout.hash);
+ if (piter) {
+ staged_ancestors.insert(**piter);
+ if (staged_ancestors.size() + package.size() > limitAncestorCount) {
+ errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
+ return false;
+ }
+ }
+ }
+ }
+ // When multiple transactions are passed in, the ancestors and descendants of all transactions
+ // considered together must be within limits even if they are not interdependent. This may be
+ // stricter than the limits for each individual transaction.
+ setEntries setAncestors;
+ const auto ret = CalculateAncestorsAndCheckLimits(total_size, package.size(),
+ setAncestors, staged_ancestors,
+ limitAncestorCount, limitAncestorSize,
+ limitDescendantCount, limitDescendantSize, errString);
+ // It's possible to overestimate the ancestor/descendant totals.
+ if (!ret) errString.insert(0, "possibly ");
+ return ret;
+}
+
+bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry,
+ setEntries &setAncestors,
+ uint64_t limitAncestorCount,
+ uint64_t limitAncestorSize,
+ uint64_t limitDescendantCount,
+ uint64_t limitDescendantSize,
+ std::string &errString,
+ bool fSearchForParents /* = true */) const
+{
+ CTxMemPoolEntry::Parents staged_ancestors;
+ const CTransaction &tx = entry.GetTx();
+
+ if (fSearchForParents) {
+ // Get parents of this transaction that are in the mempool
+ // GetMemPoolParents() is only valid for entries in the mempool, so we
+ // iterate mapTx to find parents.
+ for (unsigned int i = 0; i < tx.vin.size(); i++) {
+ std::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash);
+ if (piter) {
+ staged_ancestors.insert(**piter);
+ if (staged_ancestors.size() + 1 > limitAncestorCount) {
+ errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount);
+ return false;
+ }
+ }
+ }
+ } else {
+ // If we're not searching for parents, we require this to already be an
+ // entry in the mempool and use the entry's cached parents.
+ txiter it = mapTx.iterator_to(entry);
+ staged_ancestors = it->GetMemPoolParentsConst();
+ }
+
+ return CalculateAncestorsAndCheckLimits(entry.GetTxSize(), /* entry_count */ 1,
+ setAncestors, staged_ancestors,
+ limitAncestorCount, limitAncestorSize,
+ limitDescendantCount, limitDescendantSize, errString);
+}
+
void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors)
{
CTxMemPoolEntry::Parents parents = it->GetMemPoolParents();
diff --git a/src/txmempool.h b/src/txmempool.h
index ae4b16d377..0a84a6e6b1 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -18,6 +18,7 @@
#include <coins.h>
#include <indirectmap.h>
#include <policy/feerate.h>
+#include <policy/packages.h>
#include <primitives/transaction.h>
#include <random.h>
#include <sync.h>
@@ -585,6 +586,25 @@ private:
*/
std::set<uint256> m_unbroadcast_txids GUARDED_BY(cs);
+
+ /**
+ * Helper function to calculate all in-mempool ancestors of staged_ancestors and apply ancestor
+ * and descendant limits (including staged_ancestors thsemselves, entry_size and entry_count).
+ * param@[in] entry_size Virtual size to include in the limits.
+ * param@[in] entry_count How many entries to include in the limits.
+ * param@[in] staged_ancestors Should contain entries in the mempool.
+ * param@[out] setAncestors Will be populated with all mempool ancestors.
+ */
+ bool CalculateAncestorsAndCheckLimits(size_t entry_size,
+ size_t entry_count,
+ setEntries& setAncestors,
+ CTxMemPoolEntry::Parents &staged_ancestors,
+ uint64_t limitAncestorCount,
+ uint64_t limitAncestorSize,
+ uint64_t limitDescendantCount,
+ uint64_t limitDescendantSize,
+ std::string &errString) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
public:
indirectmap<COutPoint, const CTransaction*> mapNextTx GUARDED_BY(cs);
std::map<uint256, CAmount> mapDeltas GUARDED_BY(cs);
@@ -681,6 +701,28 @@ public:
*/
bool CalculateMemPoolAncestors(const CTxMemPoolEntry& entry, setEntries& setAncestors, uint64_t limitAncestorCount, uint64_t limitAncestorSize, uint64_t limitDescendantCount, uint64_t limitDescendantSize, std::string& errString, bool fSearchForParents = true) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+ /** Calculate all in-mempool ancestors of a set of transactions not already in the mempool and
+ * check ancestor and descendant limits. Heuristics are used to estimate the ancestor and
+ * descendant count of all entries if the package were to be added to the mempool. The limits
+ * are applied to the union of all package transactions. For example, if the package has 3
+ * transactions and limitAncestorCount = 25, the union of all 3 sets of ancestors (including the
+ * transactions themselves) must be <= 22.
+ * @param[in] package Transaction package being evaluated for acceptance
+ * to mempool. The transactions need not be direct
+ * ancestors/descendants of each other.
+ * @param[in] limitAncestorCount Max number of txns including ancestors.
+ * @param[in] limitAncestorSize Max virtual size including ancestors.
+ * @param[in] limitDescendantCount Max number of txns including descendants.
+ * @param[in] limitDescendantSize Max virtual size including descendants.
+ * @param[out] errString Populated with error reason if a limit is hit.
+ */
+ bool CheckPackageLimits(const Package& package,
+ uint64_t limitAncestorCount,
+ uint64_t limitAncestorSize,
+ uint64_t limitDescendantCount,
+ uint64_t limitDescendantSize,
+ std::string &errString) const EXCLUSIVE_LOCKS_REQUIRED(cs);
+
/** Populate setDescendants with all in-mempool descendants of hash.
* Assumes that setDescendants includes all in-mempool descendants of anything
* already in it. */
diff --git a/src/util/string.h b/src/util/string.h
index b26facc502..5617e4acc1 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -65,6 +65,14 @@ inline std::string Join(const std::vector<std::string>& list, const std::string&
}
/**
+ * Create an unordered multi-line list of items.
+ */
+inline std::string MakeUnorderedList(const std::vector<std::string>& items)
+{
+ return Join(items, "\n", [](const std::string& item) { return "- " + item; });
+}
+
+/**
* Check if a string does not contain any embedded NUL (\0) characters
*/
[[nodiscard]] inline bool ValidAsCString(const std::string& str) noexcept
diff --git a/src/util/system.cpp b/src/util/system.cpp
index 258ba2f235..30d4103819 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -502,11 +502,11 @@ bool ArgsManager::InitSettings(std::string& error)
std::vector<std::string> errors;
if (!ReadSettingsFile(&errors)) {
- error = strprintf("Failed loading settings file:\n- %s\n", Join(errors, "\n- "));
+ error = strprintf("Failed loading settings file:\n%s\n", MakeUnorderedList(errors));
return false;
}
if (!WriteSettingsFile(&errors)) {
- error = strprintf("Failed saving settings file:\n- %s\n", Join(errors, "\n- "));
+ error = strprintf("Failed saving settings file:\n%s\n", MakeUnorderedList(errors));
return false;
}
return true;
diff --git a/src/util/translation.h b/src/util/translation.h
index 99899ef3c2..62388b568c 100644
--- a/src/util/translation.h
+++ b/src/util/translation.h
@@ -28,6 +28,12 @@ struct bilingual_str {
{
return original.empty();
}
+
+ void clear()
+ {
+ original.clear();
+ translated.clear();
+ }
};
inline bilingual_str operator+(bilingual_str lhs, const bilingual_str& rhs)
diff --git a/src/validation.cpp b/src/validation.cpp
index 1b3d00bc6d..ec457da5cc 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -1079,6 +1079,19 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptMultipleTransactions(const std::
m_viewmempool.PackageAddTransaction(ws.m_ptx);
}
+ // Apply package mempool ancestor/descendant limits. Skip if there is only one transaction,
+ // because it's unnecessary. Also, CPFP carve out can increase the limit for individual
+ // transactions, but this exemption is not extended to packages in CheckPackageLimits().
+ std::string err_string;
+ if (txns.size() > 1 &&
+ !m_pool.CheckPackageLimits(txns, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants,
+ m_limit_descendant_size, err_string)) {
+ // All transactions must have individually passed mempool ancestor and descendant limits
+ // inside of PreChecks(), so this is separate from an individual transaction error.
+ package_state.Invalid(PackageValidationResult::PCKG_POLICY, "package-mempool-limits", err_string);
+ return PackageMempoolAcceptResult(package_state, std::move(results));
+ }
+
for (Workspace& ws : workspaces) {
PrecomputedTransactionData txdata;
if (!PolicyScriptChecks(args, ws, txdata)) {
diff --git a/src/validation.h b/src/validation.h
index 9d8d7c06a9..b80fa9d328 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -199,7 +199,8 @@ struct PackageMempoolAcceptResult
/**
* Map from wtxid to finished MempoolAcceptResults. The client is responsible
* for keeping track of the transaction objects themselves. If a result is not
- * present, it means validation was unfinished for that transaction.
+ * present, it means validation was unfinished for that transaction. If there
+ * was a package-wide error (see result in m_state), m_tx_results will be empty.
*/
std::map<const uint256, const MempoolAcceptResult> m_tx_results;
@@ -227,7 +228,8 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPoo
* @param[in] txns Group of transactions which may be independent or contain
* parent-child dependencies. The transactions must not conflict
* with each other, i.e., must not spend the same inputs. If any
-* dependencies exist, parents must appear before children.
+* dependencies exist, parents must appear anywhere in the list
+* before their children.
* @returns a PackageMempoolAcceptResult which includes a MempoolAcceptResult for each transaction.
* If a transaction fails, validation will exit early and some results may be missing.
*/
diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp
index e33adf94c9..2c891c3c1e 100644
--- a/src/wallet/interfaces.cpp
+++ b/src/wallet/interfaces.cpp
@@ -16,6 +16,7 @@
#include <uint256.h>
#include <util/check.h>
#include <util/system.h>
+#include <util/translation.h>
#include <util/ui_change_type.h>
#include <wallet/context.h>
#include <wallet/feebumper.h>
@@ -130,7 +131,7 @@ public:
bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) override
{
LOCK(m_wallet->cs_wallet);
- std::string error;
+ bilingual_str error;
return m_wallet->GetNewDestination(type, label, dest, error);
}
bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) override
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index ac60504419..cccaff9d65 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1760,8 +1760,10 @@ RPCHelpMan listdescriptors()
{
return RPCHelpMan{
"listdescriptors",
- "\nList descriptors imported into a descriptor-enabled wallet.",
- {},
+ "\nList descriptors imported into a descriptor-enabled wallet.\n",
+ {
+ {"private", RPCArg::Type::BOOL, RPCArg::Default{false}, "Show private descriptors."}
+ },
RPCResult{RPCResult::Type::OBJ, "", "", {
{RPCResult::Type::STR, "wallet_name", "Name of wallet this operation was performed on"},
{RPCResult::Type::ARR, "descriptors", "Array of descriptor objects",
@@ -1781,6 +1783,7 @@ RPCHelpMan listdescriptors()
}},
RPCExamples{
HelpExampleCli("listdescriptors", "") + HelpExampleRpc("listdescriptors", "")
+ + HelpExampleCli("listdescriptors", "true") + HelpExampleRpc("listdescriptors", "true")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
@@ -1791,6 +1794,11 @@ RPCHelpMan listdescriptors()
throw JSONRPCError(RPC_WALLET_ERROR, "listdescriptors is not available for non-descriptor wallets");
}
+ const bool priv = !request.params[0].isNull() && request.params[0].get_bool();
+ if (priv) {
+ EnsureWalletIsUnlocked(*wallet);
+ }
+
LOCK(wallet->cs_wallet);
UniValue descriptors(UniValue::VARR);
@@ -1804,8 +1812,9 @@ RPCHelpMan listdescriptors()
LOCK(desc_spk_man->cs_desc_man);
const auto& wallet_descriptor = desc_spk_man->GetWalletDescriptor();
std::string descriptor;
- if (!desc_spk_man->GetDescriptorString(descriptor)) {
- throw JSONRPCError(RPC_WALLET_ERROR, "Can't get normalized descriptor string.");
+
+ if (!desc_spk_man->GetDescriptorString(descriptor, priv)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Can't get descriptor string.");
}
spk.pushKV("desc", descriptor);
spk.pushKV("timestamp", wallet_descriptor.creation_time);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index f1d5117415..2e2300f887 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -266,18 +266,19 @@ static RPCHelpMan getnewaddress()
OutputType output_type = pwallet->m_default_address_type;
if (!request.params[1].isNull()) {
- if (!ParseOutputType(request.params[1].get_str(), output_type)) {
+ std::optional<OutputType> parsed = ParseOutputType(request.params[1].get_str());
+ if (!parsed) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
- }
- if (output_type == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
+ } else if (parsed.value() == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses");
}
+ output_type = parsed.value();
}
CTxDestination dest;
- std::string error;
+ bilingual_str error;
if (!pwallet->GetNewDestination(output_type, label, dest, error)) {
- throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
+ throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error.original);
}
return EncodeDestination(dest);
@@ -313,18 +314,19 @@ static RPCHelpMan getrawchangeaddress()
OutputType output_type = pwallet->m_default_change_type.value_or(pwallet->m_default_address_type);
if (!request.params[0].isNull()) {
- if (!ParseOutputType(request.params[0].get_str(), output_type)) {
+ std::optional<OutputType> parsed = ParseOutputType(request.params[0].get_str());
+ if (!parsed) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
- }
- if (output_type == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
+ } else if (parsed.value() == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses");
}
+ output_type = parsed.value();
}
CTxDestination dest;
- std::string error;
+ bilingual_str error;
if (!pwallet->GetNewChangeDestination(output_type, dest, error)) {
- throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error);
+ throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, error.original);
}
return EncodeDestination(dest);
},
@@ -1007,12 +1009,13 @@ static RPCHelpMan addmultisigaddress()
OutputType output_type = pwallet->m_default_address_type;
if (!request.params[3].isNull()) {
- if (!ParseOutputType(request.params[3].get_str(), output_type)) {
+ std::optional<OutputType> parsed = ParseOutputType(request.params[3].get_str());
+ if (!parsed) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[3].get_str()));
- }
- if (output_type == OutputType::BECH32M) {
+ } else if (parsed.value() == OutputType::BECH32M) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Bech32m multisig addresses cannot be created with legacy wallets");
}
+ output_type = parsed.value();
}
// Construct using pay-to-script-hash:
@@ -3133,11 +3136,11 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out,
if (options.exists("changeAddress") || options.exists("change_address")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both change address and address type options");
}
- OutputType out_type;
- if (!ParseOutputType(options["change_type"].get_str(), out_type)) {
+ if (std::optional<OutputType> parsed = ParseOutputType(options["change_type"].get_str())) {
+ coinControl.m_change_type.emplace(parsed.value());
+ } else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
}
- coinControl.m_change_type.emplace(out_type);
}
const UniValue include_watching_option = options.exists("include_watching") ? options["include_watching"] : options["includeWatching"];
@@ -3389,7 +3392,7 @@ RPCHelpMan signrawtransactionwithwallet()
int nHashType = ParseSighashString(request.params[2]);
// Script verification errors
- std::map<int, std::string> input_errors;
+ std::map<int, bilingual_str> input_errors;
bool complete = pwallet->SignTransaction(mtx, coins, nHashType, input_errors);
UniValue result(UniValue::VOBJ);
@@ -3872,7 +3875,7 @@ RPCHelpMan getaddressinfo()
DescriptorScriptPubKeyMan* desc_spk_man = dynamic_cast<DescriptorScriptPubKeyMan*>(pwallet->GetScriptPubKeyMan(scriptPubKey));
if (desc_spk_man) {
std::string desc_str;
- if (desc_spk_man->GetDescriptorString(desc_str)) {
+ if (desc_spk_man->GetDescriptorString(desc_str, /* priv */ false)) {
ret.pushKV("parent_desc", desc_str);
}
}
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 73433214f1..fe41f9b8cc 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -20,10 +20,10 @@
//! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details.
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
-bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error)
+bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, bilingual_str& error)
{
if (LEGACY_OUTPUT_TYPES.count(type) == 0) {
- error = _("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types").translated;
+ error = _("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types");
return false;
}
assert(type != OutputType::BECH32M);
@@ -34,7 +34,7 @@ bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestinat
// Generate a new key that is added to wallet
CPubKey new_key;
if (!GetKeyFromPool(new_key, type)) {
- error = _("Error: Keypool ran out, please call keypoolrefill first").translated;
+ error = _("Error: Keypool ran out, please call keypoolrefill first");
return false;
}
LearnRelatedScripts(new_key, type);
@@ -295,22 +295,22 @@ bool LegacyScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBat
return true;
}
-bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error)
+bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, bilingual_str& error)
{
if (LEGACY_OUTPUT_TYPES.count(type) == 0) {
- error = _("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types").translated;
+ error = _("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types");
return false;
}
assert(type != OutputType::BECH32M);
LOCK(cs_KeyStore);
if (!CanGetAddresses(internal)) {
- error = _("Error: Keypool ran out, please call keypoolrefill first").translated;
+ error = _("Error: Keypool ran out, please call keypoolrefill first");
return false;
}
if (!ReserveKeyFromKeyPool(index, keypool, internal)) {
- error = _("Error: Keypool ran out, please call keypoolrefill first").translated;
+ error = _("Error: Keypool ran out, please call keypoolrefill first");
return false;
}
address = GetDestinationForKey(keypool.vchPubKey, type);
@@ -592,7 +592,7 @@ bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sig
}
}
-bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
+bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const
{
return ::SignTransaction(tx, this, coins, sighash, input_errors);
}
@@ -1613,11 +1613,11 @@ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const
return set_address;
}
-bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error)
+bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, bilingual_str& error)
{
// Returns true if this descriptor supports getting new addresses. Conditions where we may be unable to fetch them (e.g. locked) are caught later
if (!CanGetAddresses()) {
- error = "No addresses available";
+ error = _("No addresses available");
return false;
}
{
@@ -1636,12 +1636,12 @@ bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDest
std::vector<CScript> scripts_temp;
if (m_wallet_descriptor.range_end <= m_max_cached_index && !TopUp(1)) {
// We can't generate anymore keys
- error = "Error: Keypool ran out, please call keypoolrefill first";
+ error = _("Error: Keypool ran out, please call keypoolrefill first");
return false;
}
if (!m_wallet_descriptor.descriptor->ExpandFromCache(m_wallet_descriptor.next_index, m_wallet_descriptor.cache, scripts_temp, out_keys)) {
// We can't generate anymore keys
- error = "Error: Keypool ran out, please call keypoolrefill first";
+ error = _("Error: Keypool ran out, please call keypoolrefill first");
return false;
}
@@ -1721,7 +1721,7 @@ bool DescriptorScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, Walle
return true;
}
-bool DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error)
+bool DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, bilingual_str& error)
{
LOCK(cs_desc_man);
bool result = GetNewDestination(type, address, error);
@@ -2046,7 +2046,7 @@ bool DescriptorScriptPubKeyMan::CanProvide(const CScript& script, SignatureData&
return IsMine(script);
}
-bool DescriptorScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
+bool DescriptorScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const
{
std::unique_ptr<FlatSigningProvider> keys = std::make_unique<FlatSigningProvider>();
for (const auto& coin_pair : coins) {
@@ -2258,13 +2258,20 @@ const std::vector<CScript> DescriptorScriptPubKeyMan::GetScriptPubKeys() const
return script_pub_keys;
}
-bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out) const
+bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, const bool priv) const
{
LOCK(cs_desc_man);
FlatSigningProvider provider;
provider.keys = GetKeys();
+ if (priv) {
+ // For the private version, always return the master key to avoid
+ // exposing child private keys. The risk implications of exposing child
+ // private keys together with the parent xpub may be non-obvious for users.
+ return m_wallet_descriptor.descriptor->ToPrivateString(provider, out);
+ }
+
return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, &m_wallet_descriptor.cache);
}
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index e329e0cf8f..93e1886102 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -174,14 +174,14 @@ protected:
public:
explicit ScriptPubKeyMan(WalletStorage& storage) : m_storage(storage) {}
virtual ~ScriptPubKeyMan() {};
- virtual bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) { return false; }
+ virtual bool GetNewDestination(const OutputType type, CTxDestination& dest, bilingual_str& error) { return false; }
virtual isminetype IsMine(const CScript& script) const { return ISMINE_NO; }
//! Check that the given decryption key is valid for this ScriptPubKeyMan, i.e. it decrypts all of the keys handled by it.
virtual bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) { return false; }
virtual bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) { return false; }
- virtual bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error) { return false; }
+ virtual bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, bilingual_str& error) { return false; }
virtual void KeepDestination(int64_t index, const OutputType& type) {}
virtual void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) {}
@@ -230,7 +230,7 @@ public:
virtual bool CanProvide(const CScript& script, SignatureData& sigdata) { return false; }
/** Creates new signatures and adds them to the transaction. Returns whether all inputs were signed */
- virtual bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const { return false; }
+ virtual bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const { return false; }
/** Sign a message with the given script */
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
@@ -355,13 +355,13 @@ private:
public:
using ScriptPubKeyMan::ScriptPubKeyMan;
- bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) override;
+ bool GetNewDestination(const OutputType type, CTxDestination& dest, bilingual_str& error) override;
isminetype IsMine(const CScript& script) const override;
bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) override;
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
- bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error) override;
+ bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, bilingual_str& error) override;
void KeepDestination(int64_t index, const OutputType& type) override;
void ReturnDestination(int64_t index, bool internal, const CTxDestination&) override;
@@ -396,7 +396,7 @@ public:
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
- bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
+ bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
@@ -559,13 +559,13 @@ public:
mutable RecursiveMutex cs_desc_man;
- bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) override;
+ bool GetNewDestination(const OutputType type, CTxDestination& dest, bilingual_str& error) override;
isminetype IsMine(const CScript& script) const override;
bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) override;
bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
- bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, std::string& error) override;
+ bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool, bilingual_str& error) override;
void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) override;
// Tops up the descriptor cache and m_map_script_pub_keys. The cache is stored in the wallet file
@@ -601,7 +601,7 @@ public:
bool CanProvide(const CScript& script, SignatureData& sigdata) override;
- bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
+ bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
@@ -621,7 +621,7 @@ public:
const WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
const std::vector<CScript> GetScriptPubKeys() const;
- bool GetDescriptorString(std::string& out) const;
+ bool GetDescriptorString(std::string& out, const bool priv) const;
void UpgradeDescriptorCache();
};
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 6a8df437ae..cd51ead539 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -618,9 +618,9 @@ bool CWallet::CreateTransactionInternal(
// Reserve a new key pair from key pool. If it fails, provide a dummy
// destination in case we don't need change.
CTxDestination dest;
- std::string dest_err;
+ bilingual_str dest_err;
if (!reservedest.GetReservedDestination(dest, true, dest_err)) {
- error = strprintf(_("Transaction needs a change address, but we can't generate it. %s"), dest_err);
+ error = _("Transaction needs a change address, but we can't generate it.") + Untranslated(" ") + dest_err;
}
scriptChange = GetScriptForDestination(dest);
// A valid destination implies a change script (and
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index c65ebad52f..3488ae3526 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -7,6 +7,7 @@
#include <primitives/transaction.h>
#include <random.h>
#include <test/util/setup_common.h>
+#include <util/translation.h>
#include <wallet/coincontrol.h>
#include <wallet/coinselection.h>
#include <wallet/test/wallet_test_fixture.h>
@@ -66,7 +67,7 @@ static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bo
tx.vout[nInput].nValue = nValue;
if (spendable) {
CTxDestination dest;
- std::string error;
+ bilingual_str error;
const bool destination_ok = wallet.GetNewDestination(OutputType::BECH32, "", dest, error);
assert(destination_ok);
tx.vout[nInput].scriptPubKey = GetScriptForDestination(dest);
diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp
index 66e7de4273..8821f680b3 100644
--- a/src/wallet/test/spend_tests.cpp
+++ b/src/wallet/test/spend_tests.cpp
@@ -54,7 +54,7 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup)
// Send full input minus more than the fee amount to recipient, check
// leftover input amount is paid to recipient not the miner (to_reduce ==
// -123). This overpays the recipient instead of overpaying the miner more
- // than double the neccesary fee.
+ // than double the necessary fee.
BOOST_CHECK_EQUAL(fee, check_tx(fee + 123));
}
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 75a08b6f74..c8c5215e1b 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -69,7 +69,7 @@ static CMutableTransaction TestSimpleSpend(const CTransaction& from, uint32_t in
keystore.AddKey(key);
std::map<COutPoint, Coin> coins;
coins[mtx.vin[0].prevout].out = from.vout[index];
- std::map<int, std::string> input_errors;
+ std::map<int, bilingual_str> input_errors;
BOOST_CHECK(SignTransaction(mtx, &keystore, coins, SIGHASH_ALL, input_errors));
return mtx;
}
@@ -589,7 +589,7 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
wallet->SetWalletFlag(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
BOOST_CHECK(!wallet->TopUpKeyPool(1000));
CTxDestination dest;
- std::string error;
+ bilingual_str error;
BOOST_CHECK(!wallet->GetNewDestination(OutputType::BECH32, "", dest, error));
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 9a61ca698d..e6227048d2 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -94,6 +94,16 @@ static void UpdateWalletSetting(interfaces::Chain& chain,
}
}
+/**
+ * Refresh mempool status so the wallet is in an internally consistent state and
+ * immediately knows the transaction's status: Whether it can be considered
+ * trusted and is eligible to be abandoned ...
+ */
+static void RefreshMempoolStatus(CWalletTx& tx, interfaces::Chain& chain)
+{
+ tx.fInMempool = chain.isInMempool(tx.GetHash());
+}
+
bool AddWallet(const std::shared_ptr<CWallet>& wallet)
{
LOCK(cs_wallets);
@@ -803,10 +813,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
wtx.mapValue["replaced_by_txid"] = newHash.ToString();
// Refresh mempool status without waiting for transactionRemovedFromMempool
- // notification so the wallet is in an internally consistent state and
- // immediately knows the old transaction should not be considered trusted
- // and is eligible to be abandoned
- wtx.fInMempool = chain().isInMempool(originalHash);
+ RefreshMempoolStatus(wtx, chain());
WalletBatch batch(GetDatabase());
@@ -1206,7 +1213,7 @@ void CWallet::transactionAddedToMempool(const CTransactionRef& tx, uint64_t memp
auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
- it->second.fInMempool = true;
+ RefreshMempoolStatus(it->second, chain());
}
}
@@ -1214,7 +1221,7 @@ void CWallet::transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRe
LOCK(cs_wallet);
auto it = mapWallet.find(tx->GetHash());
if (it != mapWallet.end()) {
- it->second.fInMempool = false;
+ RefreshMempoolStatus(it->second, chain());
}
// Handle transactions that were removed from the mempool because they
// conflict with transactions in a newly connected block.
@@ -1822,11 +1829,11 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const
const CWalletTx& wtx = mi->second;
coins[input.prevout] = Coin(wtx.tx->vout[input.prevout.n], wtx.m_confirm.block_height, wtx.IsCoinBase());
}
- std::map<int, std::string> input_errors;
+ std::map<int, bilingual_str> input_errors;
return SignTransaction(tx, coins, SIGHASH_DEFAULT, input_errors);
}
-bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const
+bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const
{
// Try to sign with all ScriptPubKeyMans
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
@@ -2128,7 +2135,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize)
return res;
}
-bool CWallet::GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error)
+bool CWallet::GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, bilingual_str& error)
{
LOCK(cs_wallet);
error.clear();
@@ -2138,7 +2145,7 @@ bool CWallet::GetNewDestination(const OutputType type, const std::string label,
spk_man->TopUp();
result = spk_man->GetNewDestination(type, dest, error);
} else {
- error = strprintf(_("Error: No %s addresses available."), FormatOutputType(type)).translated;
+ error = strprintf(_("Error: No %s addresses available."), FormatOutputType(type));
}
if (result) {
SetAddressBook(dest, label, "receive");
@@ -2147,7 +2154,7 @@ bool CWallet::GetNewDestination(const OutputType type, const std::string label,
return result;
}
-bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error)
+bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& dest, bilingual_str& error)
{
LOCK(cs_wallet);
error.clear();
@@ -2200,11 +2207,11 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co
return result;
}
-bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool internal, std::string& error)
+bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool internal, bilingual_str& error)
{
m_spk_man = pwallet->GetScriptPubKeyMan(type, internal);
if (!m_spk_man) {
- error = strprintf(_("Error: No %s addresses available."), FormatOutputType(type)).translated;
+ error = strprintf(_("Error: No %s addresses available."), FormatOutputType(type));
return false;
}
@@ -2586,19 +2593,21 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain* chain, const std::st
}
if (!gArgs.GetArg("-addresstype", "").empty()) {
- if (!ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
+ std::optional<OutputType> parsed = ParseOutputType(gArgs.GetArg("-addresstype", ""));
+ if (!parsed) {
error = strprintf(_("Unknown address type '%s'"), gArgs.GetArg("-addresstype", ""));
return nullptr;
}
+ walletInstance->m_default_address_type = parsed.value();
}
if (!gArgs.GetArg("-changetype", "").empty()) {
- OutputType out_type;
- if (!ParseOutputType(gArgs.GetArg("-changetype", ""), out_type)) {
+ std::optional<OutputType> parsed = ParseOutputType(gArgs.GetArg("-changetype", ""));
+ if (!parsed) {
error = strprintf(_("Unknown change type '%s'"), gArgs.GetArg("-changetype", ""));
return nullptr;
}
- walletInstance->m_default_change_type = out_type;
+ walletInstance->m_default_change_type = parsed.value();
}
if (gArgs.IsArgSet("-mintxfee")) {
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 3997751f52..25f89e8ea4 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -183,7 +183,7 @@ public:
}
//! Reserve an address
- bool GetReservedDestination(CTxDestination& pubkey, bool internal, std::string& error);
+ bool GetReservedDestination(CTxDestination& pubkey, bool internal, bilingual_str& error);
//! Return reserved address
void ReturnDestination();
//! Keep the address. Do not return it's key to the keypool when this object goes out of scope
@@ -563,7 +563,7 @@ public:
/** Fetch the inputs and sign with SIGHASH_ALL. */
bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Sign the tx given the input coins and sighash. */
- bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const;
+ bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const;
/**
@@ -665,8 +665,8 @@ public:
*/
void MarkDestinationsDirty(const std::set<CTxDestination>& destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, std::string& error);
- bool GetNewChangeDestination(const OutputType type, CTxDestination& dest, std::string& error);
+ bool GetNewDestination(const OutputType type, const std::string label, CTxDestination& dest, bilingual_str& error);
+ bool GetNewChangeDestination(const OutputType type, CTxDestination& dest, bilingual_str& error);
isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py
index c11eabc917..777787ed32 100755
--- a/test/functional/feature_block.py
+++ b/test/functional/feature_block.py
@@ -22,7 +22,7 @@ from test_framework.messages import (
CTransaction,
CTxIn,
CTxOut,
- MAX_BLOCK_BASE_SIZE,
+ MAX_BLOCK_WEIGHT,
uint256_from_compact,
uint256_from_str,
)
@@ -307,33 +307,33 @@ class FullBlockTest(BitcoinTestFramework):
b22 = self.next_block(22, spend=out[5])
self.send_blocks([b22], success=False, reject_reason='bad-txns-premature-spend-of-coinbase', reconnect=True)
- # Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected
+ # Create a block on either side of MAX_BLOCK_WEIGHT and make sure its accepted/rejected
# genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3)
# \-> b12 (3) -> b13 (4) -> b15 (5) -> b23 (6)
# \-> b24 (6) -> b25 (7)
# \-> b3 (1) -> b4 (2)
- self.log.info("Accept a block of size MAX_BLOCK_BASE_SIZE")
+ self.log.info("Accept a block of weight MAX_BLOCK_WEIGHT")
self.move_tip(15)
b23 = self.next_block(23, spend=out[6])
tx = CTransaction()
- script_length = MAX_BLOCK_BASE_SIZE - len(b23.serialize()) - 69
+ script_length = (MAX_BLOCK_WEIGHT - b23.get_weight() - 276) // 4
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b23.vtx[1].sha256, 0)))
b23 = self.update_block(23, [tx])
- # Make sure the math above worked out to produce a max-sized block
- assert_equal(len(b23.serialize()), MAX_BLOCK_BASE_SIZE)
+ # Make sure the math above worked out to produce a max-weighted block
+ assert_equal(b23.get_weight(), MAX_BLOCK_WEIGHT)
self.send_blocks([b23], True)
self.save_spendable_output()
- self.log.info("Reject a block of size MAX_BLOCK_BASE_SIZE + 1")
+ self.log.info("Reject a block of weight MAX_BLOCK_WEIGHT + 4")
self.move_tip(15)
b24 = self.next_block(24, spend=out[6])
- script_length = MAX_BLOCK_BASE_SIZE - len(b24.serialize()) - 69
+ script_length = (MAX_BLOCK_WEIGHT - b24.get_weight() - 276) // 4
script_output = CScript([b'\x00' * (script_length + 1)])
tx.vout = [CTxOut(0, script_output)]
b24 = self.update_block(24, [tx])
- assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1)
+ assert_equal(b24.get_weight(), MAX_BLOCK_WEIGHT + 1 * 4)
self.send_blocks([b24], success=False, reject_reason='bad-blk-length', reconnect=True)
b25 = self.next_block(25, spend=out[7])
@@ -373,7 +373,9 @@ class FullBlockTest(BitcoinTestFramework):
# b30 has a max-sized coinbase scriptSig.
self.move_tip(23)
b30 = self.next_block(30)
- b30.vtx[0].vin[0].scriptSig = b'\x00' * 100
+ b30.vtx[0].vin[0].scriptSig = bytes(b30.vtx[0].vin[0].scriptSig) # Convert CScript to raw bytes
+ b30.vtx[0].vin[0].scriptSig += b'\x00' * (100 - len(b30.vtx[0].vin[0].scriptSig)) # Fill with 0s
+ assert_equal(len(b30.vtx[0].vin[0].scriptSig), 100)
b30.vtx[0].rehash()
b30 = self.update_block(30, [])
self.send_blocks([b30], True)
@@ -484,13 +486,13 @@ class FullBlockTest(BitcoinTestFramework):
# Until block is full, add tx's with 1 satoshi to p2sh_script, the rest to OP_TRUE
tx_new = None
tx_last = tx
- total_size = len(b39.serialize())
- while(total_size < MAX_BLOCK_BASE_SIZE):
+ total_weight = b39.get_weight()
+ while total_weight < MAX_BLOCK_WEIGHT:
tx_new = self.create_tx(tx_last, 1, 1, p2sh_script)
tx_new.vout.append(CTxOut(tx_last.vout[1].nValue - 1, CScript([OP_TRUE])))
tx_new.rehash()
- total_size += len(tx_new.serialize())
- if total_size >= MAX_BLOCK_BASE_SIZE:
+ total_weight += tx_new.get_weight()
+ if total_weight >= MAX_BLOCK_WEIGHT:
break
b39.vtx.append(tx_new) # add tx to block
tx_last = tx_new
@@ -501,7 +503,7 @@ class FullBlockTest(BitcoinTestFramework):
# Make sure we didn't accidentally make too big a block. Note that the
# size of the block has non-determinism due to the ECDSA signature in
# the first transaction.
- while (len(b39.serialize()) >= MAX_BLOCK_BASE_SIZE):
+ while b39.get_weight() >= MAX_BLOCK_WEIGHT:
del b39.vtx[-1]
b39 = self.update_block(39, [])
@@ -833,6 +835,7 @@ class FullBlockTest(BitcoinTestFramework):
b61.vtx[0].rehash()
b61 = self.update_block(61, [])
assert_equal(duplicate_tx.serialize(), b61.vtx[0].serialize())
+ # BIP30 is always checked on regtest, regardless of the BIP34 activation height
self.send_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True)
# Test BIP30 (allow duplicate if spent)
@@ -891,7 +894,7 @@ class FullBlockTest(BitcoinTestFramework):
self.send_blocks([b63], success=False, reject_reason='bad-txns-nonfinal', reconnect=True)
# This checks that a block with a bloated VARINT between the block_header and the array of tx such that
- # the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint,
+ # the block is > MAX_BLOCK_WEIGHT with the bloated varint, but <= MAX_BLOCK_WEIGHT without the bloated varint,
# does not cause a subsequent, identical block with canonical encoding to be rejected. The test does not
# care whether the bloated block is accepted or rejected; it only cares that the second block is accepted.
#
@@ -916,12 +919,12 @@ class FullBlockTest(BitcoinTestFramework):
tx = CTransaction()
# use canonical serialization to calculate size
- script_length = MAX_BLOCK_BASE_SIZE - len(b64a.normal_serialize()) - 69
+ script_length = (MAX_BLOCK_WEIGHT - 4 * len(b64a.normal_serialize()) - 276) // 4
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0)))
b64a = self.update_block("64a", [tx])
- assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8)
+ assert_equal(b64a.get_weight(), MAX_BLOCK_WEIGHT + 8 * 4)
self.send_blocks([b64a], success=False, reject_reason='non-canonical ReadCompactSize()')
# bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently
@@ -935,7 +938,7 @@ class FullBlockTest(BitcoinTestFramework):
b64 = CBlock(b64a)
b64.vtx = copy.deepcopy(b64a.vtx)
assert_equal(b64.hash, b64a.hash)
- assert_equal(len(b64.serialize()), MAX_BLOCK_BASE_SIZE)
+ assert_equal(b64.get_weight(), MAX_BLOCK_WEIGHT)
self.blocks[64] = b64
b64 = self.update_block(64, [])
self.send_blocks([b64], True)
@@ -1269,12 +1272,12 @@ class FullBlockTest(BitcoinTestFramework):
for i in range(89, LARGE_REORG_SIZE + 89):
b = self.next_block(i, spend)
tx = CTransaction()
- script_length = MAX_BLOCK_BASE_SIZE - len(b.serialize()) - 69
+ script_length = (MAX_BLOCK_WEIGHT - b.get_weight() - 276) // 4
script_output = CScript([b'\x00' * script_length])
tx.vout.append(CTxOut(0, script_output))
tx.vin.append(CTxIn(COutPoint(b.vtx[1].sha256, 0)))
b = self.update_block(i, [tx])
- assert_equal(len(b.serialize()), MAX_BLOCK_BASE_SIZE)
+ assert_equal(b.get_weight(), MAX_BLOCK_WEIGHT)
blocks.append(b)
self.save_spendable_output()
spend = self.get_spendable_output()
diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py
index 5a23df8a13..24c8a8987a 100755
--- a/test/functional/feature_config_args.py
+++ b/test/functional/feature_config_args.py
@@ -159,8 +159,9 @@ class ConfArgsTest(BitcoinTestFramework):
self.stop_node(0)
# No peers.dat exists and -dnsseed=1
- # We expect the node will use DNS Seeds, but Regtest mode has 0 DNS seeds
- # So after 60 seconds, the node should fallback to fixed seeds (this is a slow test)
+ # We expect the node will use DNS Seeds, but Regtest mode does not have
+ # any valid DNS seeds. So after 60 seconds, the node should fallback to
+ # fixed seeds
assert not os.path.exists(os.path.join(default_data_dir, "peers.dat"))
start = int(time.time())
with self.nodes[0].assert_debug_log(expected_msgs=[
diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py
index c532300ce2..6d8e5430f8 100755
--- a/test/functional/feature_dbcrash.py
+++ b/test/functional/feature_dbcrash.py
@@ -41,7 +41,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
create_confirmed_utxos,
- hex_str_to_bytes,
)
@@ -204,7 +203,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework):
continue
for _ in range(3):
- tx.vout.append(CTxOut(output_amount, hex_str_to_bytes(utxo['scriptPubKey'])))
+ tx.vout.append(CTxOut(output_amount, bytes.fromhex(utxo['scriptPubKey'])))
# Sign and send the transaction to get into the mempool
tx_signed_hex = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py
index eb027c554a..5dd6cb6cb2 100755
--- a/test/functional/feature_dersig.py
+++ b/test/functional/feature_dersig.py
@@ -4,10 +4,11 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test BIP66 (DER SIG).
-Test that the DERSIG soft-fork activates at (regtest) height 1251.
+Test the DERSIG soft-fork activation on regtest.
"""
from test_framework.blocktools import (
+ DERSIG_HEIGHT,
create_block,
create_coinbase,
)
@@ -23,8 +24,6 @@ from test_framework.wallet import (
MiniWalletMode,
)
-DERSIG_HEIGHT = 1251
-
# A canonical signature consists of:
# <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype>
@@ -90,8 +89,10 @@ class BIP66Test(BitcoinTestFramework):
block.rehash()
block.solve()
+ assert_equal(self.nodes[0].getblockcount(), DERSIG_HEIGHT - 2)
self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
peer.send_and_ping(msg_block(block))
+ assert_equal(self.nodes[0].getblockcount(), DERSIG_HEIGHT - 1)
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py
index 9cf46d9d11..cbd8521499 100755
--- a/test/functional/feature_segwit.py
+++ b/test/functional/feature_segwit.py
@@ -46,7 +46,6 @@ from test_framework.util import (
assert_equal,
assert_is_hex_string,
assert_raises_rpc_error,
- hex_str_to_bytes,
try_rpc,
)
@@ -140,7 +139,7 @@ class SegWitTest(BitcoinTestFramework):
for i in range(3):
newaddress = self.nodes[i].getnewaddress()
self.pubkey.append(self.nodes[i].getaddressinfo(newaddress)["pubkey"])
- multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
+ multiscript = CScript([OP_1, bytes.fromhex(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG])
p2sh_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'p2sh-segwit')['address']
bip173_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'bech32')['address']
assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript))
@@ -260,8 +259,8 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(int(self.nodes[0].getmempoolentry(txid1)["wtxid"], 16), tx1.calc_sha256(True))
# Check that weight and vsize are properly reported in mempool entry (txid1)
- assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], (self.nodes[0].getmempoolentry(txid1)["weight"] + 3) // 4)
- assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], len(tx1.serialize_without_witness())*3 + len(tx1.serialize_with_witness()))
+ assert_equal(self.nodes[0].getmempoolentry(txid1)["vsize"], tx1.get_vsize())
+ assert_equal(self.nodes[0].getmempoolentry(txid1)["weight"], tx1.get_weight())
# Now create tx2, which will spend from txid1.
tx = CTransaction()
@@ -276,8 +275,8 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(int(self.nodes[0].getmempoolentry(txid2)["wtxid"], 16), tx.calc_sha256(True))
# Check that weight and vsize are properly reported in mempool entry (txid2)
- assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], (self.nodes[0].getmempoolentry(txid2)["weight"] + 3) // 4)
- assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
+ assert_equal(self.nodes[0].getmempoolentry(txid2)["vsize"], tx.get_vsize())
+ assert_equal(self.nodes[0].getmempoolentry(txid2)["weight"], tx.get_weight())
# Now create tx3, which will spend from txid2
tx = CTransaction()
@@ -299,8 +298,8 @@ class SegWitTest(BitcoinTestFramework):
assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True))
# Check that weight and vsize are properly reported in mempool entry (txid3)
- assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], (self.nodes[0].getmempoolentry(txid3)["weight"] + 3) // 4)
- assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], len(tx.serialize_without_witness())*3 + len(tx.serialize_with_witness()))
+ assert_equal(self.nodes[0].getmempoolentry(txid3)["vsize"], tx.get_vsize())
+ assert_equal(self.nodes[0].getmempoolentry(txid3)["weight"], tx.get_weight())
# Mine a block to clear the gbt cache again.
self.nodes[0].generate(1)
@@ -352,7 +351,7 @@ class SegWitTest(BitcoinTestFramework):
# Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address.
multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])['address']
- script = CScript([OP_2, hex_str_to_bytes(pubkeys[3]), hex_str_to_bytes(pubkeys[4]), OP_2, OP_CHECKMULTISIG])
+ script = CScript([OP_2, bytes.fromhex(pubkeys[3]), bytes.fromhex(pubkeys[4]), OP_2, OP_CHECKMULTISIG])
solvable_after_importaddress.append(script_to_p2sh_script(script))
for i in compressed_spendable_address:
@@ -426,7 +425,7 @@ class SegWitTest(BitcoinTestFramework):
op1 = CScript([OP_1])
op0 = CScript([OP_0])
# 2N7MGY19ti4KDMSzRfPAssP6Pxyuxoi6jLe is the P2SH(P2PKH) version of mjoE3sSrb8ByYEvgnC3Aox86u1CHnfJA4V
- unsolvable_address_key = hex_str_to_bytes("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
+ unsolvable_address_key = bytes.fromhex("02341AEC7587A51CDE5279E0630A531AEA2615A9F80B17E8D9376327BAEAA59E3D")
unsolvablep2pkh = key_to_p2pkh_script(unsolvable_address_key)
unsolvablep2wshp2pkh = script_to_p2wsh_script(unsolvablep2pkh)
p2shop0 = script_to_p2sh_script(op0)
@@ -448,11 +447,11 @@ class SegWitTest(BitcoinTestFramework):
for i in compressed_spendable_address + uncompressed_spendable_address + compressed_solvable_address + uncompressed_solvable_address:
v = self.nodes[0].getaddressinfo(i)
if (v['isscript']):
- bare = hex_str_to_bytes(v['hex'])
+ bare = bytes.fromhex(v['hex'])
importlist.append(bare.hex())
importlist.append(script_to_p2wsh_script(bare).hex())
else:
- pubkey = hex_str_to_bytes(v['pubkey'])
+ pubkey = bytes.fromhex(v['pubkey'])
p2pk = CScript([pubkey, OP_CHECKSIG])
p2pkh = key_to_p2pkh_script(pubkey)
importlist.append(p2pk.hex())
@@ -612,18 +611,18 @@ class SegWitTest(BitcoinTestFramework):
return txid
def p2sh_address_to_script(self, v):
- bare = CScript(hex_str_to_bytes(v['hex']))
- p2sh = CScript(hex_str_to_bytes(v['scriptPubKey']))
+ bare = CScript(bytes.fromhex(v['hex']))
+ p2sh = CScript(bytes.fromhex(v['scriptPubKey']))
p2wsh = script_to_p2wsh_script(bare)
p2sh_p2wsh = script_to_p2sh_script(p2wsh)
return([bare, p2sh, p2wsh, p2sh_p2wsh])
def p2pkh_address_to_script(self, v):
- pubkey = hex_str_to_bytes(v['pubkey'])
+ pubkey = bytes.fromhex(v['pubkey'])
p2wpkh = key_to_p2wpkh_script(pubkey)
p2sh_p2wpkh = script_to_p2sh_script(p2wpkh)
p2pk = CScript([pubkey, OP_CHECKSIG])
- p2pkh = CScript(hex_str_to_bytes(v['scriptPubKey']))
+ p2pkh = CScript(bytes.fromhex(v['scriptPubKey']))
p2sh_p2pk = script_to_p2sh_script(p2pk)
p2sh_p2pkh = script_to_p2sh_script(p2pkh)
p2wsh_p2pk = script_to_p2wsh_script(p2pk)
diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py
index e73ec90819..d644a420fc 100755
--- a/test/functional/interface_rest.py
+++ b/test/functional/interface_rest.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the REST API."""
-import binascii
from decimal import Decimal
from enum import Enum
from io import BytesIO
@@ -19,7 +18,6 @@ from test_framework.util import (
assert_equal,
assert_greater_than,
assert_greater_than_or_equal,
- hex_str_to_bytes,
)
from test_framework.messages import BLOCK_HEADER_SIZE
@@ -147,7 +145,7 @@ class RESTTest (BitcoinTestFramework):
bin_request = b'\x01\x02'
for txid, n in [spending, spent]:
- bin_request += hex_str_to_bytes(txid)
+ bin_request += bytes.fromhex(txid)
bin_request += pack("i", n)
bin_response = self.test_rest_request("/getutxos", http_method='POST', req_type=ReqType.BIN, body=bin_request, ret_type=RetType.BYTES)
@@ -236,13 +234,13 @@ class RESTTest (BitcoinTestFramework):
response_hex = self.test_rest_request("/block/{}".format(bb_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
assert_greater_than(int(response_hex.getheader('content-length')), BLOCK_HEADER_SIZE*2)
response_hex_bytes = response_hex.read().strip(b'\n')
- assert_equal(binascii.hexlify(response_bytes), response_hex_bytes)
+ assert_equal(response_bytes.hex().encode(), response_hex_bytes)
# Compare with hex block header
response_header_hex = self.test_rest_request("/headers/1/{}".format(bb_hash), req_type=ReqType.HEX, ret_type=RetType.OBJ)
assert_greater_than(int(response_header_hex.getheader('content-length')), BLOCK_HEADER_SIZE*2)
response_header_hex_bytes = response_header_hex.read(BLOCK_HEADER_SIZE*2)
- assert_equal(binascii.hexlify(response_bytes[:BLOCK_HEADER_SIZE]), response_header_hex_bytes)
+ assert_equal(response_bytes[:BLOCK_HEADER_SIZE].hex().encode(), response_header_hex_bytes)
# Check json format
block_json_obj = self.test_rest_request("/block/{}".format(bb_hash))
diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py
index 60c0953f6f..97d29ff197 100755
--- a/test/functional/mempool_accept.py
+++ b/test/functional/mempool_accept.py
@@ -15,7 +15,7 @@ from test_framework.messages import (
COutPoint,
CTxIn,
CTxOut,
- MAX_BLOCK_BASE_SIZE,
+ MAX_BLOCK_WEIGHT,
MAX_MONEY,
tx_from_hex,
)
@@ -209,7 +209,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
self.log.info('A really large transaction')
tx = tx_from_hex(raw_tx_reference)
- tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_BASE_SIZE / len(tx.vin[0].serialize()))
+ tx.vin = [tx.vin[0]] * math.ceil(MAX_BLOCK_WEIGHT // 4 / len(tx.vin[0].serialize()))
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'bad-txns-oversize'}],
rawtxs=[tx.serialize().hex()],
diff --git a/test/functional/mempool_accept_wtxid.py b/test/functional/mempool_accept_wtxid.py
index 63ecc8ee2a..ffafe7428f 100755
--- a/test/functional/mempool_accept_wtxid.py
+++ b/test/functional/mempool_accept_wtxid.py
@@ -4,9 +4,10 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""
Test mempool acceptance in case of an already known transaction
-with identical non-witness data different witness.
+with identical non-witness data but different witness.
"""
+from copy import deepcopy
from test_framework.messages import (
COIN,
COutPoint,
@@ -79,10 +80,7 @@ class MempoolWtxidTest(BitcoinTestFramework):
child_one_txid = child_one.rehash()
# Create another identical transaction with witness solving second branch
- child_two = CTransaction()
- child_two.vin.append(CTxIn(COutPoint(int(parent_txid, 16), 0), b""))
- child_two.vout.append(CTxOut(int(9.99996 * COIN), child_script_pubkey))
- child_two.wit.vtxinwit.append(CTxInWitness())
+ child_two = deepcopy(child_one)
child_two.wit.vtxinwit[0].scriptWitness.stack = [b'', witness_script]
child_two_wtxid = child_two.getwtxid()
child_two_txid = child_two.rehash()
@@ -104,8 +102,7 @@ class MempoolWtxidTest(BitcoinTestFramework):
"allowed": False,
"reject-reason": "txn-already-in-mempool"
}])
- testres_child_two = node.testmempoolaccept([child_two.serialize().hex()])[0]
- assert_equal(testres_child_two, {
+ assert_equal(node.testmempoolaccept([child_two.serialize().hex()])[0], {
"txid": child_two_txid,
"wtxid": child_two_wtxid,
"allowed": False,
diff --git a/test/functional/mempool_package_limits.py b/test/functional/mempool_package_limits.py
new file mode 100755
index 0000000000..749ec6aa77
--- /dev/null
+++ b/test/functional/mempool_package_limits.py
@@ -0,0 +1,475 @@
+#!/usr/bin/env python3
+# Copyright (c) 2021 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test logic for limiting mempool and package ancestors/descendants."""
+
+from decimal import Decimal
+
+from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.messages import (
+ COIN,
+ CTransaction,
+ CTxInWitness,
+ tx_from_hex,
+ WITNESS_SCALE_FACTOR,
+)
+from test_framework.script import (
+ CScript,
+ OP_TRUE,
+)
+from test_framework.util import (
+ assert_equal,
+)
+from test_framework.wallet import (
+ bulk_transaction,
+ create_child_with_parents,
+ make_chain,
+)
+
+class MempoolPackageLimitsTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def run_test(self):
+ self.log.info("Generate blocks to create UTXOs")
+ node = self.nodes[0]
+ self.privkeys = [node.get_deterministic_priv_key().key]
+ self.address = node.get_deterministic_priv_key().address
+ self.coins = []
+ # The last 100 coinbase transactions are premature
+ for b in node.generatetoaddress(200, self.address)[:100]:
+ coinbase = node.getblock(blockhash=b, verbosity=2)["tx"][0]
+ self.coins.append({
+ "txid": coinbase["txid"],
+ "amount": coinbase["vout"][0]["value"],
+ "scriptPubKey": coinbase["vout"][0]["scriptPubKey"],
+ })
+
+ self.test_chain_limits()
+ self.test_desc_count_limits()
+ self.test_anc_count_limits()
+ self.test_anc_count_limits_2()
+ self.test_anc_count_limits_bushy()
+
+ # The node will accept our (nonstandard) extra large OP_RETURN outputs
+ self.restart_node(0, extra_args=["-acceptnonstdtxn=1"])
+ self.test_anc_size_limits()
+ self.test_desc_size_limits()
+
+ def test_chain_limits_helper(self, mempool_count, package_count):
+ node = self.nodes[0]
+ assert_equal(0, node.getmempoolinfo()["size"])
+ first_coin = self.coins.pop()
+ spk = None
+ txid = first_coin["txid"]
+ chain_hex = []
+ chain_txns = []
+ value = first_coin["amount"]
+
+ for i in range(mempool_count + package_count):
+ (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk)
+ txid = tx.rehash()
+ if i < mempool_count:
+ node.sendrawtransaction(txhex)
+ assert_equal(node.getrawmempool(verbose=True)[txid]["ancestorcount"], i + 1)
+ else:
+ chain_hex.append(txhex)
+ chain_txns.append(tx)
+ testres_too_long = node.testmempoolaccept(rawtxs=chain_hex)
+ for txres in testres_too_long:
+ assert_equal(txres["package-error"], "package-mempool-limits")
+
+ # Clear mempool and check that the package passes now
+ node.generate(1)
+ assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=chain_hex)])
+
+ def test_chain_limits(self):
+ """Create chains from mempool and package transactions that are longer than 25,
+ but only if both in-mempool and in-package transactions are considered together.
+ This checks that both mempool and in-package transactions are taken into account when
+ calculating ancestors/descendant limits.
+ """
+ self.log.info("Check that in-package ancestors count for mempool ancestor limits")
+
+ # 24 transactions in the mempool and 2 in the package. The parent in the package has
+ # 24 in-mempool ancestors and 1 in-package descendant. The child has 0 direct parents
+ # in the mempool, but 25 in-mempool and in-package ancestors in total.
+ self.test_chain_limits_helper(24, 2)
+ # 2 transactions in the mempool and 24 in the package.
+ self.test_chain_limits_helper(2, 24)
+ # 13 transactions in the mempool and 13 in the package.
+ self.test_chain_limits_helper(13, 13)
+
+ def test_desc_count_limits(self):
+ """Create an 'A' shaped package with 24 transactions in the mempool and 2 in the package:
+ M1
+ ^ ^
+ M2a M2b
+ . .
+ . .
+ . .
+ M12a ^
+ ^ M13b
+ ^ ^
+ Pa Pb
+ The top ancestor in the package exceeds descendant limits but only if the in-mempool and in-package
+ descendants are all considered together (24 including in-mempool descendants and 26 including both
+ package transactions).
+ """
+ node = self.nodes[0]
+ assert_equal(0, node.getmempoolinfo()["size"])
+ self.log.info("Check that in-mempool and in-package descendants are calculated properly in packages")
+ # Top parent in mempool, M1
+ first_coin = self.coins.pop()
+ parent_value = (first_coin["amount"] - Decimal("0.0002")) / 2 # Deduct reasonable fee and make 2 outputs
+ inputs = [{"txid": first_coin["txid"], "vout": 0}]
+ outputs = [{self.address : parent_value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE : parent_value}]
+ rawtx = node.createrawtransaction(inputs, outputs)
+
+ parent_signed = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys)
+ assert parent_signed["complete"]
+ parent_tx = tx_from_hex(parent_signed["hex"])
+ parent_txid = parent_tx.rehash()
+ node.sendrawtransaction(parent_signed["hex"])
+
+ package_hex = []
+
+ # Chain A
+ spk = parent_tx.vout[0].scriptPubKey.hex()
+ value = parent_value
+ txid = parent_txid
+ for i in range(12):
+ (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk)
+ txid = tx.rehash()
+ if i < 11: # M2a... M12a
+ node.sendrawtransaction(txhex)
+ else: # Pa
+ package_hex.append(txhex)
+
+ # Chain B
+ value = parent_value - Decimal("0.0001")
+ rawtx_b = node.createrawtransaction([{"txid": parent_txid, "vout": 1}], {self.address : value})
+ tx_child_b = tx_from_hex(rawtx_b) # M2b
+ tx_child_b.wit.vtxinwit = [CTxInWitness()]
+ tx_child_b.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
+ tx_child_b_hex = tx_child_b.serialize().hex()
+ node.sendrawtransaction(tx_child_b_hex)
+ spk = tx_child_b.vout[0].scriptPubKey.hex()
+ txid = tx_child_b.rehash()
+ for i in range(12):
+ (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk)
+ txid = tx.rehash()
+ if i < 11: # M3b... M13b
+ node.sendrawtransaction(txhex)
+ else: # Pb
+ package_hex.append(txhex)
+
+ assert_equal(24, node.getmempoolinfo()["size"])
+ assert_equal(2, len(package_hex))
+ testres_too_long = node.testmempoolaccept(rawtxs=package_hex)
+ for txres in testres_too_long:
+ assert_equal(txres["package-error"], "package-mempool-limits")
+
+ # Clear mempool and check that the package passes now
+ node.generate(1)
+ assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=package_hex)])
+
+ def test_anc_count_limits(self):
+ """Create a 'V' shaped chain with 24 transactions in the mempool and 3 in the package:
+ M1a M1b
+ ^ ^
+ M2a M2b
+ . .
+ . .
+ . .
+ M12a M12b
+ ^ ^
+ Pa Pb
+ ^ ^
+ Pc
+ The lowest descendant, Pc, exceeds ancestor limits, but only if the in-mempool
+ and in-package ancestors are all considered together.
+ """
+ node = self.nodes[0]
+ assert_equal(0, node.getmempoolinfo()["size"])
+ package_hex = []
+ parents_tx = []
+ values = []
+ scripts = []
+
+ self.log.info("Check that in-mempool and in-package ancestors are calculated properly in packages")
+
+ # Two chains of 13 transactions each
+ for _ in range(2):
+ spk = None
+ top_coin = self.coins.pop()
+ txid = top_coin["txid"]
+ value = top_coin["amount"]
+ for i in range(13):
+ (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk)
+ txid = tx.rehash()
+ if i < 12:
+ node.sendrawtransaction(txhex)
+ else: # Save the 13th transaction for the package
+ package_hex.append(txhex)
+ parents_tx.append(tx)
+ scripts.append(spk)
+ values.append(value)
+
+ # Child Pc
+ child_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, scripts)
+ package_hex.append(child_hex)
+
+ assert_equal(24, node.getmempoolinfo()["size"])
+ assert_equal(3, len(package_hex))
+ testres_too_long = node.testmempoolaccept(rawtxs=package_hex)
+ for txres in testres_too_long:
+ assert_equal(txres["package-error"], "package-mempool-limits")
+
+ # Clear mempool and check that the package passes now
+ node.generate(1)
+ assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=package_hex)])
+
+ def test_anc_count_limits_2(self):
+ """Create a 'Y' shaped chain with 24 transactions in the mempool and 2 in the package:
+ M1a M1b
+ ^ ^
+ M2a M2b
+ . .
+ . .
+ . .
+ M12a M12b
+ ^ ^
+ Pc
+ ^
+ Pd
+ The lowest descendant, Pd, exceeds ancestor limits, but only if the in-mempool
+ and in-package ancestors are all considered together.
+ """
+ node = self.nodes[0]
+ assert_equal(0, node.getmempoolinfo()["size"])
+ parents_tx = []
+ values = []
+ scripts = []
+
+ self.log.info("Check that in-mempool and in-package ancestors are calculated properly in packages")
+ # Two chains of 12 transactions each
+ for _ in range(2):
+ spk = None
+ top_coin = self.coins.pop()
+ txid = top_coin["txid"]
+ value = top_coin["amount"]
+ for i in range(12):
+ (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk)
+ txid = tx.rehash()
+ value -= Decimal("0.0001")
+ node.sendrawtransaction(txhex)
+ if i == 11:
+ # last 2 transactions will be the parents of Pc
+ parents_tx.append(tx)
+ values.append(value)
+ scripts.append(spk)
+
+ # Child Pc
+ pc_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, scripts)
+ pc_tx = tx_from_hex(pc_hex)
+ pc_value = sum(values) - Decimal("0.0002")
+ pc_spk = pc_tx.vout[0].scriptPubKey.hex()
+
+ # Child Pd
+ (_, pd_hex, _, _) = make_chain(node, self.address, self.privkeys, pc_tx.rehash(), pc_value, 0, pc_spk)
+
+ assert_equal(24, node.getmempoolinfo()["size"])
+ testres_too_long = node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])
+ for txres in testres_too_long:
+ assert_equal(txres["package-error"], "package-mempool-limits")
+
+ # Clear mempool and check that the package passes now
+ node.generate(1)
+ assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])])
+
+ def test_anc_count_limits_bushy(self):
+ """Create a tree with 20 transactions in the mempool and 6 in the package:
+ M1...M4 M5...M8 M9...M12 M13...M16 M17...M20
+ ^ ^ ^ ^ ^ (each with 4 parents)
+ P0 P1 P2 P3 P4
+ ^ ^ ^ ^ ^ (5 parents)
+ PC
+ Where M(4i+1)...M+(4i+4) are the parents of Pi and P0, P1, P2, P3, and P4 are the parents of PC.
+ P0... P4 individually only have 4 parents each, and PC has no in-mempool parents. But
+ combined, PC has 25 in-mempool and in-package parents.
+ """
+ node = self.nodes[0]
+ assert_equal(0, node.getmempoolinfo()["size"])
+ package_hex = []
+ parent_txns = []
+ parent_values = []
+ scripts = []
+ for _ in range(5): # Make package transactions P0 ... P4
+ gp_tx = []
+ gp_values = []
+ gp_scripts = []
+ for _ in range(4): # Make mempool transactions M(4i+1)...M(4i+4)
+ parent_coin = self.coins.pop()
+ value = parent_coin["amount"]
+ txid = parent_coin["txid"]
+ (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value)
+ gp_tx.append(tx)
+ gp_values.append(value)
+ gp_scripts.append(spk)
+ node.sendrawtransaction(txhex)
+ # Package transaction Pi
+ pi_hex = create_child_with_parents(node, self.address, self.privkeys, gp_tx, gp_values, gp_scripts)
+ package_hex.append(pi_hex)
+ pi_tx = tx_from_hex(pi_hex)
+ parent_txns.append(pi_tx)
+ parent_values.append(Decimal(pi_tx.vout[0].nValue) / COIN)
+ scripts.append(pi_tx.vout[0].scriptPubKey.hex())
+ # Package transaction PC
+ package_hex.append(create_child_with_parents(node, self.address, self.privkeys, parent_txns, parent_values, scripts))
+
+ assert_equal(20, node.getmempoolinfo()["size"])
+ assert_equal(6, len(package_hex))
+ testres = node.testmempoolaccept(rawtxs=package_hex)
+ for txres in testres:
+ assert_equal(txres["package-error"], "package-mempool-limits")
+
+ # Clear mempool and check that the package passes now
+ node.generate(1)
+ assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=package_hex)])
+
+ def test_anc_size_limits(self):
+ """Test Case with 2 independent transactions in the mempool and a parent + child in the
+ package, where the package parent is the child of both mempool transactions (30KvB each):
+ A B
+ ^ ^
+ C
+ ^
+ D
+ The lowest descendant, D, exceeds ancestor size limits, but only if the in-mempool
+ and in-package ancestors are all considered together.
+ """
+ node = self.nodes[0]
+ assert_equal(0, node.getmempoolinfo()["size"])
+ parents_tx = []
+ values = []
+ scripts = []
+ target_weight = WITNESS_SCALE_FACTOR * 1000 * 30 # 30KvB
+ high_fee = Decimal("0.003") # 10 sats/vB
+ self.log.info("Check that in-mempool and in-package ancestor size limits are calculated properly in packages")
+ # Mempool transactions A and B
+ for _ in range(2):
+ spk = None
+ top_coin = self.coins.pop()
+ txid = top_coin["txid"]
+ value = top_coin["amount"]
+ (tx, _, _, _) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk, high_fee)
+ bulked_tx = bulk_transaction(tx, node, target_weight, self.privkeys)
+ node.sendrawtransaction(bulked_tx.serialize().hex())
+ parents_tx.append(bulked_tx)
+ values.append(Decimal(bulked_tx.vout[0].nValue) / COIN)
+ scripts.append(bulked_tx.vout[0].scriptPubKey.hex())
+
+ # Package transaction C
+ small_pc_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, scripts, high_fee)
+ pc_tx = bulk_transaction(tx_from_hex(small_pc_hex), node, target_weight, self.privkeys)
+ pc_value = Decimal(pc_tx.vout[0].nValue) / COIN
+ pc_spk = pc_tx.vout[0].scriptPubKey.hex()
+ pc_hex = pc_tx.serialize().hex()
+
+ # Package transaction D
+ (small_pd, _, val, spk) = make_chain(node, self.address, self.privkeys, pc_tx.rehash(), pc_value, 0, pc_spk, high_fee)
+ prevtxs = [{
+ "txid": pc_tx.rehash(),
+ "vout": 0,
+ "scriptPubKey": spk,
+ "amount": val,
+ }]
+ pd_tx = bulk_transaction(small_pd, node, target_weight, self.privkeys, prevtxs)
+ pd_hex = pd_tx.serialize().hex()
+
+ assert_equal(2, node.getmempoolinfo()["size"])
+ testres_too_heavy = node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])
+ for txres in testres_too_heavy:
+ assert_equal(txres["package-error"], "package-mempool-limits")
+
+ # Clear mempool and check that the package passes now
+ node.generate(1)
+ assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])])
+
+ def test_desc_size_limits(self):
+ """Create 3 mempool transactions and 2 package transactions (25KvB each):
+ Ma
+ ^ ^
+ Mb Mc
+ ^ ^
+ Pd Pe
+ The top ancestor in the package exceeds descendant size limits but only if the in-mempool
+ and in-package descendants are all considered together.
+ """
+ node = self.nodes[0]
+ assert_equal(0, node.getmempoolinfo()["size"])
+ target_weight = 21 * 1000 * WITNESS_SCALE_FACTOR
+ high_fee = Decimal("0.0021") # 10 sats/vB
+ self.log.info("Check that in-mempool and in-package descendant sizes are calculated properly in packages")
+ # Top parent in mempool, Ma
+ first_coin = self.coins.pop()
+ parent_value = (first_coin["amount"] - high_fee) / 2 # Deduct fee and make 2 outputs
+ inputs = [{"txid": first_coin["txid"], "vout": 0}]
+ outputs = [{self.address : parent_value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE: parent_value}]
+ rawtx = node.createrawtransaction(inputs, outputs)
+ parent_tx = bulk_transaction(tx_from_hex(rawtx), node, target_weight, self.privkeys)
+ node.sendrawtransaction(parent_tx.serialize().hex())
+
+ package_hex = []
+ for j in range(2): # Two legs (left and right)
+ # Mempool transaction (Mb and Mc)
+ mempool_tx = CTransaction()
+ spk = parent_tx.vout[j].scriptPubKey.hex()
+ value = Decimal(parent_tx.vout[j].nValue) / COIN
+ txid = parent_tx.rehash()
+ prevtxs = [{
+ "txid": txid,
+ "vout": j,
+ "scriptPubKey": spk,
+ "amount": value,
+ }]
+ if j == 0: # normal key
+ (tx_small, _, _, _) = make_chain(node, self.address, self.privkeys, txid, value, j, spk, high_fee)
+ mempool_tx = bulk_transaction(tx_small, node, target_weight, self.privkeys, prevtxs)
+ else: # OP_TRUE
+ inputs = [{"txid": txid, "vout": 1}]
+ outputs = {self.address: value - high_fee}
+ small_tx = tx_from_hex(node.createrawtransaction(inputs, outputs))
+ mempool_tx = bulk_transaction(small_tx, node, target_weight, None, prevtxs)
+ node.sendrawtransaction(mempool_tx.serialize().hex())
+
+ # Package transaction (Pd and Pe)
+ spk = mempool_tx.vout[0].scriptPubKey.hex()
+ value = Decimal(mempool_tx.vout[0].nValue) / COIN
+ txid = mempool_tx.rehash()
+ (tx_small, _, _, _) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk, high_fee)
+ prevtxs = [{
+ "txid": txid,
+ "vout": 0,
+ "scriptPubKey": spk,
+ "amount": value,
+ }]
+ package_tx = bulk_transaction(tx_small, node, target_weight, self.privkeys, prevtxs)
+ package_hex.append(package_tx.serialize().hex())
+
+ assert_equal(3, node.getmempoolinfo()["size"])
+ assert_equal(2, len(package_hex))
+ testres_too_heavy = node.testmempoolaccept(rawtxs=package_hex)
+ for txres in testres_too_heavy:
+ assert_equal(txres["package-error"], "package-mempool-limits")
+
+ # Clear mempool and check that the package passes now
+ node.generate(1)
+ assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=package_hex)])
+
+if __name__ == "__main__":
+ MempoolPackageLimitsTest().main()
diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py
index 1426fdaacb..9fc38ebf53 100755
--- a/test/functional/mining_prioritisetransaction.py
+++ b/test/functional/mining_prioritisetransaction.py
@@ -6,7 +6,7 @@
import time
-from test_framework.messages import COIN, MAX_BLOCK_BASE_SIZE
+from test_framework.messages import COIN, MAX_BLOCK_WEIGHT
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_raises_rpc_error, create_confirmed_utxos, create_lots_of_big_transactions, gen_return_txouts
@@ -61,15 +61,15 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[start_range:end_range], end_range - start_range, (i+1)*base_fee)
# Make sure that the size of each group of transactions exceeds
- # MAX_BLOCK_BASE_SIZE -- otherwise the test needs to be revised to create
- # more transactions.
+ # MAX_BLOCK_WEIGHT // 4 -- otherwise the test needs to be revised to
+ # create more transactions.
mempool = self.nodes[0].getrawmempool(True)
sizes = [0, 0, 0]
for i in range(3):
for j in txids[i]:
assert j in mempool
sizes[i] += mempool[j]['vsize']
- assert sizes[i] > MAX_BLOCK_BASE_SIZE # Fail => raise utxo_count
+ assert sizes[i] > MAX_BLOCK_WEIGHT // 4 # Fail => raise utxo_count
# add a fee delta to something in the cheapest bucket and make sure it gets mined
# also check that a different entry in the cheapest bucket is NOT mined
diff --git a/test/functional/p2p_addr_relay.py b/test/functional/p2p_addr_relay.py
index ff1d85a9be..113e1f9492 100755
--- a/test/functional/p2p_addr_relay.py
+++ b/test/functional/p2p_addr_relay.py
@@ -11,14 +11,15 @@ from test_framework.messages import (
NODE_NETWORK,
NODE_WITNESS,
msg_addr,
- msg_getaddr
+ msg_getaddr,
+ msg_verack
)
from test_framework.p2p import (
P2PInterface,
p2p_lock,
)
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal
+from test_framework.util import assert_equal, assert_greater_than
import random
import time
@@ -27,10 +28,12 @@ class AddrReceiver(P2PInterface):
num_ipv4_received = 0
test_addr_contents = False
_tokens = 1
+ send_getaddr = True
- def __init__(self, test_addr_contents=False):
+ def __init__(self, test_addr_contents=False, send_getaddr=True):
super().__init__()
self.test_addr_contents = test_addr_contents
+ self.send_getaddr = send_getaddr
def on_addr(self, message):
for addr in message.addrs:
@@ -60,6 +63,11 @@ class AddrReceiver(P2PInterface):
def addr_received(self):
return self.num_ipv4_received != 0
+ def on_version(self, message):
+ self.send_message(msg_verack())
+ if (self.send_getaddr):
+ self.send_message(msg_getaddr())
+
def getaddr_received(self):
return self.message_count['getaddr'] > 0
@@ -75,6 +83,10 @@ class AddrTest(BitcoinTestFramework):
def run_test(self):
self.oversized_addr_test()
self.relay_tests()
+ self.inbound_blackhole_tests()
+
+ # This test populates the addrman, which can impact the node's behavior
+ # in subsequent tests
self.getaddr_tests()
self.blocksonly_mode_tests()
self.rate_limit_tests()
@@ -156,7 +168,7 @@ class AddrTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
self.log.info('Check relay of addresses received from outbound peers')
- inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver(test_addr_contents=True))
+ inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver(test_addr_contents=True, send_getaddr=False))
full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type="outbound-full-relay")
msg = self.setup_addr_msg(2)
self.send_addr_msg(full_outbound_peer, msg, [inbound_peer])
@@ -167,6 +179,9 @@ class AddrTest(BitcoinTestFramework):
# of the outbound peer which is often sent before the GETADDR response.
assert_equal(inbound_peer.num_ipv4_received, 0)
+ # Send an empty ADDR message to initialize address relay on this connection.
+ inbound_peer.send_and_ping(msg_addr())
+
self.log.info('Check that subsequent addr messages sent from an outbound peer are relayed')
msg2 = self.setup_addr_msg(2)
self.send_addr_msg(full_outbound_peer, msg2, [inbound_peer])
@@ -184,7 +199,64 @@ class AddrTest(BitcoinTestFramework):
self.nodes[0].disconnect_p2ps()
+ def sum_addr_messages(self, msgs_dict):
+ return sum(bytes_received for (msg, bytes_received) in msgs_dict.items() if msg in ['addr', 'addrv2', 'getaddr'])
+
+ def inbound_blackhole_tests(self):
+ self.log.info('Check that we only relay addresses to inbound peers who have previously sent us addr related messages')
+
+ addr_source = self.nodes[0].add_p2p_connection(P2PInterface())
+ receiver_peer = self.nodes[0].add_p2p_connection(AddrReceiver())
+ blackhole_peer = self.nodes[0].add_p2p_connection(AddrReceiver(send_getaddr=False))
+ initial_addrs_received = receiver_peer.num_ipv4_received
+
+ peerinfo = self.nodes[0].getpeerinfo()
+ assert_equal(peerinfo[0]['addr_relay_enabled'], True) # addr_source
+ assert_equal(peerinfo[1]['addr_relay_enabled'], True) # receiver_peer
+ assert_equal(peerinfo[2]['addr_relay_enabled'], False) # blackhole_peer
+
+ # addr_source sends 2 addresses to node0
+ msg = self.setup_addr_msg(2)
+ addr_source.send_and_ping(msg)
+ self.mocktime += 30 * 60
+ self.nodes[0].setmocktime(self.mocktime)
+ receiver_peer.sync_with_ping()
+ blackhole_peer.sync_with_ping()
+
+ peerinfo = self.nodes[0].getpeerinfo()
+
+ # Confirm node received addr-related messages from receiver peer
+ assert_greater_than(self.sum_addr_messages(peerinfo[1]['bytesrecv_per_msg']), 0)
+ # And that peer received addresses
+ assert_equal(receiver_peer.num_ipv4_received - initial_addrs_received, 2)
+
+ # Confirm node has not received addr-related messages from blackhole peer
+ assert_equal(self.sum_addr_messages(peerinfo[2]['bytesrecv_per_msg']), 0)
+ # And that peer did not receive addresses
+ assert_equal(blackhole_peer.num_ipv4_received, 0)
+
+ self.log.info("After blackhole peer sends addr message, it becomes eligible for addr gossip")
+ blackhole_peer.send_and_ping(msg_addr())
+
+ # Confirm node has now received addr-related messages from blackhole peer
+ assert_greater_than(self.sum_addr_messages(peerinfo[1]['bytesrecv_per_msg']), 0)
+ assert_equal(self.nodes[0].getpeerinfo()[2]['addr_relay_enabled'], True)
+
+ msg = self.setup_addr_msg(2)
+ self.send_addr_msg(addr_source, msg, [receiver_peer, blackhole_peer])
+
+ # And that peer received addresses
+ assert_equal(blackhole_peer.num_ipv4_received, 2)
+
+ self.nodes[0].disconnect_p2ps()
+
def getaddr_tests(self):
+ # In the previous tests, the node answered GETADDR requests with an
+ # empty addrman. Due to GETADDR response caching (see
+ # CConnman::GetAddresses), the node would continue to provide 0 addrs
+ # in response until enough time has passed or the node is restarted.
+ self.restart_node(0)
+
self.log.info('Test getaddr behavior')
self.log.info('Check that we send a getaddr message upon connecting to an outbound-full-relay peer')
full_outbound_peer = self.nodes[0].add_outbound_p2p_connection(AddrReceiver(), p2p_idx=0, connection_type="outbound-full-relay")
@@ -197,7 +269,7 @@ class AddrTest(BitcoinTestFramework):
assert_equal(block_relay_peer.getaddr_received(), False)
self.log.info('Check that we answer getaddr messages only from inbound peers')
- inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver())
+ inbound_peer = self.nodes[0].add_p2p_connection(AddrReceiver(send_getaddr=False))
inbound_peer.sync_with_ping()
# Add some addresses to addrman
diff --git a/test/functional/p2p_dns_seeds.py b/test/functional/p2p_dns_seeds.py
new file mode 100755
index 0000000000..e58ad8e0fc
--- /dev/null
+++ b/test/functional/p2p_dns_seeds.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python3
+# Copyright (c) 2021 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test ThreadDNSAddressSeed logic for querying DNS seeds."""
+
+import itertools
+
+from test_framework.p2p import P2PInterface
+from test_framework.test_framework import BitcoinTestFramework
+
+
+class P2PDNSSeeds(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 1
+ self.extra_args = [["-dnsseed=1"]]
+
+ def run_test(self):
+ self.init_arg_tests()
+ self.existing_outbound_connections_test()
+ self.existing_block_relay_connections_test()
+ self.force_dns_test()
+ self.wait_time_tests()
+
+ def init_arg_tests(self):
+ fakeaddr = "fakenodeaddr.fakedomain.invalid."
+
+ self.log.info("Check that setting -connect disables -dnsseed by default")
+ self.nodes[0].stop_node()
+ with self.nodes[0].assert_debug_log(expected_msgs=["DNS seeding disabled"]):
+ self.start_node(0, [f"-connect={fakeaddr}"])
+
+ self.log.info("Check that running -connect and -dnsseed means DNS logic runs.")
+ with self.nodes[0].assert_debug_log(expected_msgs=["Loading addresses from DNS seed"], timeout=12):
+ self.restart_node(0, [f"-connect={fakeaddr}", "-dnsseed=1"])
+
+ self.log.info("Check that running -forcednsseed and -dnsseed=0 throws an error.")
+ self.nodes[0].stop_node()
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg="Error: Cannot set -forcednsseed to true when setting -dnsseed to false.",
+ extra_args=["-forcednsseed=1", "-dnsseed=0"],
+ )
+
+ self.log.info("Check that running -forcednsseed and -connect throws an error.")
+ # -connect soft sets -dnsseed to false, so throws the same error
+ self.nodes[0].stop_node()
+ self.nodes[0].assert_start_raises_init_error(
+ expected_msg="Error: Cannot set -forcednsseed to true when setting -dnsseed to false.",
+ extra_args=["-forcednsseed=1", f"-connect={fakeaddr}"],
+ )
+
+ # Restore default bitcoind settings
+ self.restart_node(0)
+
+ def existing_outbound_connections_test(self):
+ # Make sure addrman is populated to enter the conditional where we
+ # delay and potentially skip DNS seeding.
+ self.nodes[0].addpeeraddress("192.0.0.8", 8333)
+
+ self.log.info("Check that we *do not* query DNS seeds if we have 2 outbound connections")
+
+ self.restart_node(0)
+ with self.nodes[0].assert_debug_log(expected_msgs=["P2P peers available. Skipped DNS seeding."], timeout=12):
+ for i in range(2):
+ self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i, connection_type="outbound-full-relay")
+
+ def existing_block_relay_connections_test(self):
+ # Make sure addrman is populated to enter the conditional where we
+ # delay and potentially skip DNS seeding. No-op when run after
+ # existing_outbound_connections_test.
+ self.nodes[0].addpeeraddress("192.0.0.8", 8333)
+
+ self.log.info("Check that we *do* query DNS seeds if we only have 2 block-relay-only connections")
+
+ self.restart_node(0)
+ with self.nodes[0].assert_debug_log(expected_msgs=["Loading addresses from DNS seed"], timeout=12):
+ # This mimics the "anchors" logic where nodes are likely to
+ # reconnect to block-relay-only connections on startup.
+ # Since we do not participate in addr relay with these connections,
+ # we still want to query the DNS seeds.
+ for i in range(2):
+ self.nodes[0].add_outbound_p2p_connection(P2PInterface(), p2p_idx=i, connection_type="block-relay-only")
+
+ def force_dns_test(self):
+ self.log.info("Check that we query DNS seeds if -forcednsseed param is set")
+
+ with self.nodes[0].assert_debug_log(expected_msgs=["Loading addresses from DNS seed"], timeout=12):
+ # -dnsseed defaults to 1 in bitcoind, but 0 in the test framework,
+ # so pass it explicitly here
+ self.restart_node(0, ["-forcednsseed", "-dnsseed=1"])
+
+ # Restore default for subsequent tests
+ self.restart_node(0)
+
+ def wait_time_tests(self):
+ self.log.info("Check the delay before querying DNS seeds")
+
+ # Populate addrman with < 1000 addresses
+ for i in range(5):
+ a = f"192.0.0.{i}"
+ self.nodes[0].addpeeraddress(a, 8333)
+
+ # The delay should be 11 seconds
+ with self.nodes[0].assert_debug_log(expected_msgs=["Waiting 11 seconds before querying DNS seeds.\n"]):
+ self.restart_node(0)
+
+ # Populate addrman with > 1000 addresses
+ for i in itertools.count():
+ first_octet = i % 2 + 1
+ second_octet = i % 256
+ third_octet = i % 100
+ a = f"{first_octet}.{second_octet}.{third_octet}.1"
+ self.nodes[0].addpeeraddress(a, 8333)
+ if (i > 1000 and i % 100 == 0):
+ # The addrman size is non-deterministic because new addresses
+ # are sorted into buckets, potentially displacing existing
+ # addresses. Periodically check if we have met the desired
+ # threshold.
+ if len(self.nodes[0].getnodeaddresses(0)) > 1000:
+ break
+
+ # The delay should be 5 mins
+ with self.nodes[0].assert_debug_log(expected_msgs=["Waiting 300 seconds before querying DNS seeds.\n"]):
+ self.restart_node(0)
+
+
+if __name__ == '__main__':
+ P2PDNSSeeds().main()
diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py
index 9c34506320..f3b80abb59 100755
--- a/test/functional/p2p_invalid_messages.py
+++ b/test/functional/p2p_invalid_messages.py
@@ -28,7 +28,6 @@ from test_framework.p2p import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- hex_str_to_bytes,
)
VALID_DATA_LIMIT = MAX_PROTOCOL_MESSAGE_LENGTH - 5 # Account for the 5-byte length prefix
@@ -187,7 +186,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
[
'received: addrv2 (1 bytes)',
],
- hex_str_to_bytes('00'))
+ bytes.fromhex('00'))
def test_addrv2_too_long_address(self):
self.test_addrv2('too long address',
@@ -196,7 +195,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
'ProcessMessages(addrv2, 525 bytes): Exception',
'Address too long: 513 > 512',
],
- hex_str_to_bytes(
+ bytes.fromhex(
'01' + # number of entries
'61bc6649' + # time, Fri Jan 9 02:54:25 UTC 2009
'00' + # service flags, COMPACTSIZE(NODE_NONE)
@@ -213,7 +212,7 @@ class InvalidMessagesTest(BitcoinTestFramework):
'IP 9.9.9.9 mapped',
'Added 1 addresses',
],
- hex_str_to_bytes(
+ bytes.fromhex(
'02' + # number of entries
# this should be ignored without impeding acceptance of subsequent ones
now_hex + # time
diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py
index db96e6bdcf..e5093855ff 100755
--- a/test/functional/p2p_segwit.py
+++ b/test/functional/p2p_segwit.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test segwit transactions and blocks on P2P network."""
from decimal import Decimal
-import math
import random
import struct
import time
@@ -21,7 +20,7 @@ from test_framework.messages import (
CTxInWitness,
CTxOut,
CTxWitness,
- MAX_BLOCK_BASE_SIZE,
+ MAX_BLOCK_WEIGHT,
MSG_BLOCK,
MSG_TX,
MSG_WITNESS_FLAG,
@@ -81,7 +80,6 @@ from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
softfork_active,
- hex_str_to_bytes,
assert_raises_rpc_error,
)
@@ -101,22 +99,12 @@ class UTXO():
self.nValue = value
def sign_p2pk_witness_input(script, tx_to, in_idx, hashtype, value, key):
- """Add signature for a P2PK witness program."""
+ """Add signature for a P2PK witness script."""
tx_hash = SegwitV0SignatureHash(script, tx_to, in_idx, hashtype, value)
signature = key.sign_ecdsa(tx_hash) + chr(hashtype).encode('latin-1')
tx_to.wit.vtxinwit[in_idx].scriptWitness.stack = [signature, script]
tx_to.rehash()
-def get_virtual_size(witness_block):
- """Calculate the virtual size of a witness block.
-
- Virtual size is base + witness/4."""
- base_size = len(witness_block.serialize(with_witness=False))
- total_size = len(witness_block.serialize())
- # the "+3" is so we round up
- vsize = int((3 * base_size + total_size + 3) / 4)
- return vsize
-
def test_transaction_acceptance(node, p2p, tx, with_witness, accepted, reason=None):
"""Send a transaction to the node and check that it's accepted to the mempool
@@ -272,7 +260,7 @@ class SegWitTest(BitcoinTestFramework):
self.test_submit_block()
self.test_extra_witness_data()
self.test_max_witness_push_length()
- self.test_max_witness_program_length()
+ self.test_max_witness_script_length()
self.test_witness_input_length()
self.test_block_relay()
self.test_tx_relay_after_segwit_activation()
@@ -415,7 +403,7 @@ class SegWitTest(BitcoinTestFramework):
block = self.test_node.request_block(block_hash, 2)
wit_block = self.test_node.request_block(block_hash, 2 | MSG_WITNESS_FLAG)
assert_equal(block.serialize(), wit_block.serialize())
- assert_equal(block.serialize(), hex_str_to_bytes(rpc_block))
+ assert_equal(block.serialize(), bytes.fromhex(rpc_block))
else:
# After activation, witness blocks and non-witness blocks should
# be different. Verify rpc getblock() returns witness blocks, while
@@ -430,7 +418,7 @@ class SegWitTest(BitcoinTestFramework):
rpc_block = self.nodes[0].getblock(block.hash, False)
non_wit_block = self.test_node.request_block(block.sha256, 2)
wit_block = self.test_node.request_block(block.sha256, 2 | MSG_WITNESS_FLAG)
- assert_equal(wit_block.serialize(), hex_str_to_bytes(rpc_block))
+ assert_equal(wit_block.serialize(), bytes.fromhex(rpc_block))
assert_equal(wit_block.serialize(False), non_wit_block.serialize())
assert_equal(wit_block.serialize(), block.serialize())
@@ -438,8 +426,7 @@ class SegWitTest(BitcoinTestFramework):
rpc_details = self.nodes[0].getblock(block.hash, True)
assert_equal(rpc_details["size"], len(block.serialize()))
assert_equal(rpc_details["strippedsize"], len(block.serialize(False)))
- weight = 3 * len(block.serialize(False)) + len(block.serialize())
- assert_equal(rpc_details["weight"], weight)
+ assert_equal(rpc_details["weight"], block.get_weight())
# Upgraded node should not ask for blocks from unupgraded
block4 = self.build_next_block(version=4)
@@ -473,8 +460,8 @@ class SegWitTest(BitcoinTestFramework):
blocks are permitted to contain witnesses)."""
# Create two outputs, a p2wsh and p2sh-p2wsh
- witness_program = CScript([OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
p2sh_script_pubkey = script_to_p2sh_script(script_pubkey)
value = self.utxo[0].nValue // 3
@@ -581,9 +568,9 @@ class SegWitTest(BitcoinTestFramework):
V0 segwit outputs and inputs are always standard.
V0 segwit inputs may only be mined after activation, but not before."""
- witness_program = CScript([OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
- p2sh_script_pubkey = script_to_p2sh_script(witness_program)
+ witness_script = CScript([OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
+ p2sh_script_pubkey = script_to_p2sh_script(witness_script)
# First prepare a p2sh output (so that spending it will pass standardness)
p2sh_tx = CTransaction()
@@ -599,7 +586,7 @@ class SegWitTest(BitcoinTestFramework):
# Now test standardness of v0 P2WSH outputs.
# Start by creating a transaction with two outputs.
tx = CTransaction()
- tx.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))]
+ tx.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_script]))]
tx.vout = [CTxOut(p2sh_tx.vout[0].nValue - 10000, script_pubkey)]
tx.vout.append(CTxOut(8000, script_pubkey)) # Might burn this later
tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER # Just to have the option to bump this tx from the mempool
@@ -610,14 +597,14 @@ class SegWitTest(BitcoinTestFramework):
test_transaction_acceptance(self.nodes[1], self.std_node, tx, with_witness=True, accepted=True)
# Now create something that looks like a P2PKH output. This won't be spendable.
- witness_hash = sha256(witness_program)
+ witness_hash = sha256(witness_script)
script_pubkey = CScript([OP_0, hash160(witness_hash)])
tx2 = CTransaction()
# tx was accepted, so we spend the second output.
tx2.vin = [CTxIn(COutPoint(tx.sha256, 1), b"")]
tx2.vout = [CTxOut(7000, script_pubkey)]
tx2.wit.vtxinwit.append(CTxInWitness())
- tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_script]
tx2.rehash()
test_transaction_acceptance(self.nodes[1], self.std_node, tx2, with_witness=True, accepted=True)
@@ -630,7 +617,7 @@ class SegWitTest(BitcoinTestFramework):
tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")]
tx3.vout = [CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))]
tx3.wit.vtxinwit.append(CTxInWitness())
- tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
+ tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_script]
tx3.rehash()
if not self.segwit_active:
# Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed
@@ -688,8 +675,8 @@ class SegWitTest(BitcoinTestFramework):
"""Test P2SH wrapped witness programs."""
# Prepare the p2sh-wrapped witness output
- witness_program = CScript([OP_DROP, OP_TRUE])
- p2wsh_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_DROP, OP_TRUE])
+ p2wsh_pubkey = script_to_p2wsh_script(witness_script)
script_pubkey = script_to_p2sh_script(p2wsh_pubkey)
script_sig = CScript([p2wsh_pubkey]) # a push of the redeem script
@@ -733,7 +720,7 @@ class SegWitTest(BitcoinTestFramework):
spend_tx.vin[0].scriptSig = script_sig
spend_tx.rehash()
spend_tx.wit.vtxinwit.append(CTxInWitness())
- spend_tx.wit.vtxinwit[0].scriptWitness.stack = [b'a', witness_program]
+ spend_tx.wit.vtxinwit[0].scriptWitness.stack = [b'a', witness_script]
# Verify mempool acceptance
test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=True, accepted=True)
@@ -782,18 +769,18 @@ class SegWitTest(BitcoinTestFramework):
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
- # Let's construct a witness program
- witness_program = CScript([OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ # Let's construct a witness script
+ witness_script = CScript([OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey))
tx.rehash()
# tx2 will spend tx1, and send back to a regular anyone-can-spend address
tx2 = CTransaction()
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
- tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, witness_program))
+ tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, witness_script))
tx2.wit.vtxinwit.append(CTxInWitness())
- tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_script]
tx2.rehash()
block_3 = self.build_next_block()
@@ -828,7 +815,7 @@ class SegWitTest(BitcoinTestFramework):
block_4 = self.build_next_block()
tx3 = CTransaction()
tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b""))
- tx3.vout.append(CTxOut(tx.vout[0].nValue - 1000, witness_program))
+ tx3.vout.append(CTxOut(tx.vout[0].nValue - 1000, witness_script))
tx3.rehash()
block_4.vtx.append(tx3)
block_4.hashMerkleRoot = block_4.calc_merkle_root()
@@ -850,7 +837,7 @@ class SegWitTest(BitcoinTestFramework):
block.solve()
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.append(b'a' * 5000000)
- assert get_virtual_size(block) > MAX_BLOCK_BASE_SIZE
+ assert block.get_weight() > MAX_BLOCK_WEIGHT
# We can't send over the p2p network, because this is too big to relay
# TODO: repeat this test with a block that can be relayed
@@ -859,7 +846,7 @@ class SegWitTest(BitcoinTestFramework):
assert self.nodes[0].getbestblockhash() != block.hash
block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.pop()
- assert get_virtual_size(block) < MAX_BLOCK_BASE_SIZE
+ assert block.get_weight() < MAX_BLOCK_WEIGHT
assert_equal(None, self.nodes[0].submitblock(block.serialize().hex()))
assert self.nodes[0].getbestblockhash() == block.hash
@@ -890,14 +877,14 @@ class SegWitTest(BitcoinTestFramework):
assert len(self.utxo) > 0
# Create a P2WSH transaction.
- # The witness program will be a bunch of OP_2DROP's, followed by OP_TRUE.
+ # The witness script will be a bunch of OP_2DROP's, followed by OP_TRUE.
# This should give us plenty of room to tweak the spending tx's
# virtual size.
NUM_DROPS = 200 # 201 max ops per script!
NUM_OUTPUTS = 50
- witness_program = CScript([OP_2DROP] * NUM_DROPS + [OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_2DROP] * NUM_DROPS + [OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
prevout = COutPoint(self.utxo[0].sha256, self.utxo[0].n)
value = self.utxo[0].nValue
@@ -917,15 +904,14 @@ class SegWitTest(BitcoinTestFramework):
child_tx.vout = [CTxOut(value - 100000, CScript([OP_TRUE]))]
for _ in range(NUM_OUTPUTS):
child_tx.wit.vtxinwit.append(CTxInWitness())
- child_tx.wit.vtxinwit[-1].scriptWitness.stack = [b'a' * 195] * (2 * NUM_DROPS) + [witness_program]
+ child_tx.wit.vtxinwit[-1].scriptWitness.stack = [b'a' * 195] * (2 * NUM_DROPS) + [witness_script]
child_tx.rehash()
self.update_witness_block_with_transactions(block, [parent_tx, child_tx])
- vsize = get_virtual_size(block)
- additional_bytes = (MAX_BLOCK_BASE_SIZE - vsize) * 4
+ additional_bytes = MAX_BLOCK_WEIGHT - block.get_weight()
i = 0
while additional_bytes > 0:
- # Add some more bytes to each input until we hit MAX_BLOCK_BASE_SIZE+1
+ # Add some more bytes to each input until we hit MAX_BLOCK_WEIGHT+1
extra_bytes = min(additional_bytes + 1, 55)
block.vtx[-1].wit.vtxinwit[int(i / (2 * NUM_DROPS))].scriptWitness.stack[i % (2 * NUM_DROPS)] = b'a' * (195 + extra_bytes)
additional_bytes -= extra_bytes
@@ -934,8 +920,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx[0].vout.pop() # Remove old commitment
add_witness_commitment(block)
block.solve()
- vsize = get_virtual_size(block)
- assert_equal(vsize, MAX_BLOCK_BASE_SIZE + 1)
+ assert_equal(block.get_weight(), MAX_BLOCK_WEIGHT + 1)
# Make sure that our test case would exceed the old max-network-message
# limit
assert len(block.serialize()) > 2 * 1024 * 1024
@@ -948,7 +933,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx[0].vout.pop()
add_witness_commitment(block)
block.solve()
- assert get_virtual_size(block) == MAX_BLOCK_BASE_SIZE
+ assert block.get_weight() == MAX_BLOCK_WEIGHT
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
@@ -998,8 +983,8 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
- witness_program = CScript([OP_DROP, OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_DROP, OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
# First try extra witness data on a tx that doesn't require a witness
tx = CTransaction()
@@ -1030,7 +1015,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 1), b"")) # non-witness
tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE])))
tx2.wit.vtxinwit.extend([CTxInWitness(), CTxInWitness()])
- tx2.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), CScript([CScriptNum(1)]), witness_program]
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), CScript([CScriptNum(1)]), witness_script]
tx2.wit.vtxinwit[1].scriptWitness.stack = [CScript([OP_TRUE])]
block = self.build_next_block()
@@ -1070,8 +1055,8 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
- witness_program = CScript([OP_DROP, OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_DROP, OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
@@ -1083,7 +1068,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE])))
tx2.wit.vtxinwit.append(CTxInWitness())
# First try a 521-byte stack element
- tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a' * (MAX_SCRIPT_ELEMENT_SIZE + 1), witness_program]
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a' * (MAX_SCRIPT_ELEMENT_SIZE + 1), witness_script]
tx2.rehash()
self.update_witness_block_with_transactions(block, [tx, tx2])
@@ -1101,15 +1086,15 @@ class SegWitTest(BitcoinTestFramework):
self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue))
@subtest # type: ignore
- def test_max_witness_program_length(self):
+ def test_max_witness_script_length(self):
"""Test that witness outputs greater than 10kB can't be spent."""
- MAX_PROGRAM_LENGTH = 10000
+ MAX_WITNESS_SCRIPT_LENGTH = 10000
- # This program is 19 max pushes (9937 bytes), then 64 more opcode-bytes.
- long_witness_program = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 63 + [OP_TRUE])
- assert len(long_witness_program) == MAX_PROGRAM_LENGTH + 1
- long_script_pubkey = script_to_p2wsh_script(long_witness_program)
+ # This script is 19 max pushes (9937 bytes), then 64 more opcode-bytes.
+ long_witness_script = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 63 + [OP_TRUE])
+ assert len(long_witness_script) == MAX_WITNESS_SCRIPT_LENGTH + 1
+ long_script_pubkey = script_to_p2wsh_script(long_witness_script)
block = self.build_next_block()
@@ -1122,22 +1107,22 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE])))
tx2.wit.vtxinwit.append(CTxInWitness())
- tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a'] * 44 + [long_witness_program]
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a'] * 44 + [long_witness_script]
tx2.rehash()
self.update_witness_block_with_transactions(block, [tx, tx2])
test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
- # Try again with one less byte in the witness program
- witness_program = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 62 + [OP_TRUE])
- assert len(witness_program) == MAX_PROGRAM_LENGTH
- script_pubkey = script_to_p2wsh_script(witness_program)
+ # Try again with one less byte in the witness script
+ witness_script = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 62 + [OP_TRUE])
+ assert len(witness_script) == MAX_WITNESS_SCRIPT_LENGTH
+ script_pubkey = script_to_p2wsh_script(witness_script)
tx.vout[0] = CTxOut(tx.vout[0].nValue, script_pubkey)
tx.rehash()
tx2.vin[0].prevout.hash = tx.sha256
- tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a'] * 43 + [witness_program]
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a'] * 43 + [witness_script]
tx2.rehash()
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx, tx2])
@@ -1150,8 +1135,8 @@ class SegWitTest(BitcoinTestFramework):
def test_witness_input_length(self):
"""Test that vin length must match vtxinwit length."""
- witness_program = CScript([OP_DROP, OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_DROP, OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
# Create a transaction that splits our utxo into many outputs
tx = CTransaction()
@@ -1195,7 +1180,7 @@ class SegWitTest(BitcoinTestFramework):
# First try using a too long vtxinwit
for i in range(11):
tx2.wit.vtxinwit.append(CTxInWitness())
- tx2.wit.vtxinwit[i].scriptWitness.stack = [b'a', witness_program]
+ tx2.wit.vtxinwit[i].scriptWitness.stack = [b'a', witness_script]
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx2])
@@ -1211,15 +1196,15 @@ class SegWitTest(BitcoinTestFramework):
# Now make one of the intermediate witnesses be incorrect
tx2.wit.vtxinwit.append(CTxInWitness())
- tx2.wit.vtxinwit[-1].scriptWitness.stack = [b'a', witness_program]
- tx2.wit.vtxinwit[5].scriptWitness.stack = [witness_program]
+ tx2.wit.vtxinwit[-1].scriptWitness.stack = [b'a', witness_script]
+ tx2.wit.vtxinwit[5].scriptWitness.stack = [witness_script]
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
# Fix the broken witness and the block should be accepted.
- tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_program]
+ tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_script]
block.vtx = [block.vtx[0]]
self.update_witness_block_with_transactions(block, [tx2])
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
@@ -1257,8 +1242,8 @@ class SegWitTest(BitcoinTestFramework):
test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=False, accepted=True)
# Now try to add extra witness data to a valid witness tx.
- witness_program = CScript([OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
tx2 = CTransaction()
tx2.vin.append(CTxIn(COutPoint(tx_hash, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_pubkey))
@@ -1269,10 +1254,10 @@ class SegWitTest(BitcoinTestFramework):
tx3.wit.vtxinwit.append(CTxInWitness())
# Add too-large for IsStandard witness and check that it does not enter reject filter
- p2sh_program = CScript([OP_TRUE])
- witness_program2 = CScript([b'a' * 400000])
- tx3.vout.append(CTxOut(tx2.vout[0].nValue - 1000, script_to_p2sh_script(p2sh_program)))
- tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program2]
+ p2sh_script = CScript([OP_TRUE])
+ witness_script2 = CScript([b'a' * 400000])
+ tx3.vout.append(CTxOut(tx2.vout[0].nValue - 1000, script_to_p2sh_script(p2sh_script)))
+ tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_script2]
tx3.rehash()
# Node will not be blinded to the transaction, requesting it any number of times
@@ -1286,14 +1271,14 @@ class SegWitTest(BitcoinTestFramework):
# Remove witness stuffing, instead add extra witness push on stack
tx3.vout[0] = CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))
- tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program]
+ tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_script]
tx3.rehash()
test_transaction_acceptance(self.nodes[0], self.test_node, tx2, with_witness=True, accepted=True)
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=False)
# Get rid of the extra witness, and verify acceptance.
- tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
+ tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_script]
# Also check that old_node gets a tx announcement, even though this is
# a witness transaction.
self.old_node.wait_for_inv([CInv(MSG_TX, tx2.sha256)]) # wait until tx2 was inv'ed
@@ -1305,12 +1290,11 @@ class SegWitTest(BitcoinTestFramework):
raw_tx = self.nodes[0].getrawtransaction(tx3.hash, 1)
assert_equal(int(raw_tx["hash"], 16), tx3.calc_sha256(True))
assert_equal(raw_tx["size"], len(tx3.serialize_with_witness()))
- weight = len(tx3.serialize_with_witness()) + 3 * len(tx3.serialize_without_witness())
- vsize = math.ceil(weight / 4)
+ vsize = tx3.get_vsize()
assert_equal(raw_tx["vsize"], vsize)
- assert_equal(raw_tx["weight"], weight)
+ assert_equal(raw_tx["weight"], tx3.get_weight())
assert_equal(len(raw_tx["vin"][0]["txinwitness"]), 1)
- assert_equal(raw_tx["vin"][0]["txinwitness"][0], witness_program.hex())
+ assert_equal(raw_tx["vin"][0]["txinwitness"][0], witness_script.hex())
assert vsize != raw_tx["size"]
# Cleanup: mine the transactions and update utxo for next test
@@ -1346,8 +1330,8 @@ class SegWitTest(BitcoinTestFramework):
self.sync_blocks()
temp_utxo = []
tx = CTransaction()
- witness_program = CScript([OP_TRUE])
- witness_hash = sha256(witness_program)
+ witness_script = CScript([OP_TRUE])
+ witness_hash = sha256(witness_script)
assert_equal(len(self.nodes[1].getrawmempool()), 0)
for version in list(range(OP_1, OP_16 + 1)) + [OP_0]:
# First try to spend to a future version segwit script_pubkey.
@@ -1375,7 +1359,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")]
tx2.vout = [CTxOut(tx.vout[0].nValue - 1000, script_pubkey)]
tx2.wit.vtxinwit.append(CTxInWitness())
- tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_script]
tx2.rehash()
# Gets accepted to both policy-enforcing nodes and others.
test_transaction_acceptance(self.nodes[0], self.test_node, tx2, with_witness=True, accepted=True)
@@ -1390,7 +1374,7 @@ class SegWitTest(BitcoinTestFramework):
tx3.vin.append(CTxIn(COutPoint(i.sha256, i.n), b""))
tx3.wit.vtxinwit.append(CTxInWitness())
total_value += i.nValue
- tx3.wit.vtxinwit[-1].scriptWitness.stack = [witness_program]
+ tx3.wit.vtxinwit[-1].scriptWitness.stack = [witness_script]
tx3.vout.append(CTxOut(total_value - 1000, script_pubkey))
tx3.rehash()
@@ -1419,8 +1403,8 @@ class SegWitTest(BitcoinTestFramework):
block = self.build_next_block()
# Change the output of the block to be a witness output.
- witness_program = CScript([OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
block.vtx[0].vout[0].scriptPubKey = script_pubkey
# This next line will rehash the coinbase and update the merkle
# root, and solve.
@@ -1429,9 +1413,9 @@ class SegWitTest(BitcoinTestFramework):
spend_tx = CTransaction()
spend_tx.vin = [CTxIn(COutPoint(block.vtx[0].sha256, 0), b"")]
- spend_tx.vout = [CTxOut(block.vtx[0].vout[0].nValue, witness_program)]
+ spend_tx.vout = [CTxOut(block.vtx[0].vout[0].nValue, witness_script)]
spend_tx.wit.vtxinwit.append(CTxInWitness())
- spend_tx.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
+ spend_tx.wit.vtxinwit[0].scriptWitness.stack = [witness_script]
spend_tx.rehash()
# Now test a premature spend.
@@ -1480,8 +1464,8 @@ class SegWitTest(BitcoinTestFramework):
# Now try to spend it. Send it to a P2WSH output, which we'll
# use in the next test.
- witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
- script_wsh = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
+ script_wsh = script_to_p2wsh_script(witness_script)
tx2 = CTransaction()
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
@@ -1510,7 +1494,7 @@ class SegWitTest(BitcoinTestFramework):
tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), b""))
tx3.vout.append(CTxOut(tx2.vout[0].nValue - 1000, script_p2sh))
tx3.wit.vtxinwit.append(CTxInWitness())
- sign_p2pk_witness_input(witness_program, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key)
+ sign_p2pk_witness_input(witness_script, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key)
# Should fail policy test.
test_transaction_acceptance(self.nodes[0], self.test_node, tx3, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
@@ -1527,7 +1511,7 @@ class SegWitTest(BitcoinTestFramework):
tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), script_sig))
tx4.vout.append(CTxOut(tx3.vout[0].nValue - 1000, script_pubkey))
tx4.wit.vtxinwit.append(CTxInWitness())
- sign_p2pk_witness_input(witness_program, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key)
+ sign_p2pk_witness_input(witness_script, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key)
# Should fail policy test.
test_transaction_acceptance(self.nodes[0], self.test_node, tx4, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)')
@@ -1558,8 +1542,8 @@ class SegWitTest(BitcoinTestFramework):
key.generate()
pubkey = key.get_pubkey().get_bytes()
- witness_program = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([pubkey, CScriptOp(OP_CHECKSIG)])
+ script_pubkey = script_to_p2wsh_script(witness_script)
# First create a witness output for use in the tests.
tx = CTransaction()
@@ -1586,18 +1570,18 @@ class SegWitTest(BitcoinTestFramework):
tx.vout.append(CTxOut(prev_utxo.nValue - 1000, script_pubkey))
tx.wit.vtxinwit.append(CTxInWitness())
# Too-large input value
- sign_p2pk_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue + 1, key)
+ sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue + 1, key)
self.update_witness_block_with_transactions(block, [tx])
test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
# Too-small input value
- sign_p2pk_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue - 1, key)
+ sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue - 1, key)
block.vtx.pop() # remove last tx
self.update_witness_block_with_transactions(block, [tx])
test_witness_block(self.nodes[0], self.test_node, block, accepted=False)
# Now try correct value
- sign_p2pk_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue, key)
+ sign_p2pk_witness_input(witness_script, tx, 0, hashtype, prev_utxo.nValue, key)
block.vtx.pop()
self.update_witness_block_with_transactions(block, [tx])
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
@@ -1618,7 +1602,7 @@ class SegWitTest(BitcoinTestFramework):
for _ in range(NUM_SIGHASH_TESTS):
tx.vout.append(CTxOut(split_value, script_pubkey))
tx.wit.vtxinwit.append(CTxInWitness())
- sign_p2pk_witness_input(witness_program, tx, 0, SIGHASH_ALL, prev_utxo.nValue, key)
+ sign_p2pk_witness_input(witness_script, tx, 0, SIGHASH_ALL, prev_utxo.nValue, key)
for i in range(NUM_SIGHASH_TESTS):
temp_utxos.append(UTXO(tx.sha256, i, split_value))
@@ -1653,7 +1637,7 @@ class SegWitTest(BitcoinTestFramework):
if random.randint(0, 1):
anyonecanpay = SIGHASH_ANYONECANPAY
hashtype = random.randint(1, 3) | anyonecanpay
- sign_p2pk_witness_input(witness_program, tx, i, hashtype, temp_utxos[i].nValue, key)
+ sign_p2pk_witness_input(witness_script, tx, i, hashtype, temp_utxos[i].nValue, key)
if (hashtype == SIGHASH_SINGLE and i >= num_outputs):
used_sighash_single_out_of_bounds = True
tx.rehash()
@@ -1664,7 +1648,7 @@ class SegWitTest(BitcoinTestFramework):
block.vtx.append(tx)
# Test the block periodically, if we're close to maxblocksize
- if (get_virtual_size(block) > MAX_BLOCK_BASE_SIZE - 1000):
+ if block.get_weight() > MAX_BLOCK_WEIGHT - 4000:
self.update_witness_block_with_transactions(block, [])
test_witness_block(self.nodes[0], self.test_node, block, accepted=True)
block = self.build_next_block()
@@ -1683,7 +1667,7 @@ class SegWitTest(BitcoinTestFramework):
tx.vin.append(CTxIn(COutPoint(temp_utxos[0].sha256, temp_utxos[0].n), b""))
tx.vout.append(CTxOut(temp_utxos[0].nValue, script_pkh))
tx.wit.vtxinwit.append(CTxInWitness())
- sign_p2pk_witness_input(witness_program, tx, 0, SIGHASH_ALL, temp_utxos[0].nValue, key)
+ sign_p2pk_witness_input(witness_script, tx, 0, SIGHASH_ALL, temp_utxos[0].nValue, key)
tx2 = CTransaction()
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue, CScript([OP_TRUE])))
@@ -1723,7 +1707,7 @@ class SegWitTest(BitcoinTestFramework):
# the signatures as we go.
tx.vin.append(CTxIn(COutPoint(i.sha256, i.n), b""))
tx.wit.vtxinwit.append(CTxInWitness())
- sign_p2pk_witness_input(witness_program, tx, index, SIGHASH_ALL | SIGHASH_ANYONECANPAY, i.nValue, key)
+ sign_p2pk_witness_input(witness_script, tx, index, SIGHASH_ALL | SIGHASH_ANYONECANPAY, i.nValue, key)
index += 1
block = self.build_next_block()
self.update_witness_block_with_transactions(block, [tx])
@@ -1889,8 +1873,8 @@ class SegWitTest(BitcoinTestFramework):
"""Test sigop counting is correct inside witnesses."""
# Keep this under MAX_OPS_PER_SCRIPT (201)
- witness_program = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKMULTISIG] * 5 + [OP_CHECKSIG] * 193 + [OP_ENDIF])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKMULTISIG] * 5 + [OP_CHECKSIG] * 193 + [OP_ENDIF])
+ script_pubkey = script_to_p2wsh_script(witness_script)
sigops_per_script = 20 * 5 + 193 * 1
# We'll produce 2 extra outputs, one with a program that would take us
@@ -1905,13 +1889,13 @@ class SegWitTest(BitcoinTestFramework):
# This script, when spent with the first
# N(=MAX_SIGOP_COST//sigops_per_script) outputs of our transaction,
# would push us just over the block sigop limit.
- witness_program_toomany = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG] * (extra_sigops_available + 1) + [OP_ENDIF])
- script_pubkey_toomany = script_to_p2wsh_script(witness_program_toomany)
+ witness_script_toomany = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG] * (extra_sigops_available + 1) + [OP_ENDIF])
+ script_pubkey_toomany = script_to_p2wsh_script(witness_script_toomany)
# If we spend this script instead, we would exactly reach our sigop
# limit (for witness sigops).
- witness_program_justright = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG] * (extra_sigops_available) + [OP_ENDIF])
- script_pubkey_justright = script_to_p2wsh_script(witness_program_justright)
+ witness_script_justright = CScript([OP_TRUE, OP_IF, OP_TRUE, OP_ELSE] + [OP_CHECKSIG] * (extra_sigops_available) + [OP_ENDIF])
+ script_pubkey_justright = script_to_p2wsh_script(witness_script_justright)
# First split our available utxo into a bunch of outputs
split_value = self.utxo[0].nValue // outputs
@@ -1934,9 +1918,9 @@ class SegWitTest(BitcoinTestFramework):
for i in range(outputs - 1):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, i), b""))
tx2.wit.vtxinwit.append(CTxInWitness())
- tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_program]
+ tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_script]
total_value += tx.vout[i].nValue
- tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_program_toomany]
+ tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_script_toomany]
tx2.vout.append(CTxOut(total_value, CScript([OP_TRUE])))
tx2.rehash()
@@ -1975,7 +1959,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vout.pop()
tx2.vin.append(CTxIn(COutPoint(tx.sha256, outputs - 1), b""))
tx2.wit.vtxinwit.append(CTxInWitness())
- tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_program_justright]
+ tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_script_justright]
tx2.rehash()
self.update_witness_block_with_transactions(block_5, [tx2])
test_witness_block(self.nodes[0], self.test_node, block_5, accepted=True)
@@ -2043,8 +2027,8 @@ class SegWitTest(BitcoinTestFramework):
# Create a Segwit output from the latest UTXO
# and announce it to the network
- witness_program = CScript([OP_TRUE])
- script_pubkey = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_TRUE])
+ script_pubkey = script_to_p2wsh_script(witness_script)
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b""))
@@ -2056,7 +2040,7 @@ class SegWitTest(BitcoinTestFramework):
tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b""))
tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, script_pubkey))
tx2.wit.vtxinwit.append(CTxInWitness())
- tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program]
+ tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_script]
tx2.rehash()
# Announce Segwit transaction with wtxid
diff --git a/test/functional/rpc_addresses_deprecation.py b/test/functional/rpc_addresses_deprecation.py
index ac430f5b39..251cc85ae9 100755
--- a/test/functional/rpc_addresses_deprecation.py
+++ b/test/functional/rpc_addresses_deprecation.py
@@ -10,7 +10,6 @@ from test_framework.messages import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- hex_str_to_bytes
)
@@ -36,7 +35,7 @@ class AddressesDeprecationTest(BitcoinTestFramework):
# This transaction is derived from test/util/data/txcreatemultisig1.json
tx = tx_from_hex(signed)
- tx.vout[0].scriptPubKey = hex_str_to_bytes("522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae")
+ tx.vout[0].scriptPubKey = bytes.fromhex("522102a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff39721021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d2102df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb48553ae")
tx_signed = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
txid = node.sendrawtransaction(hexstring=tx_signed, maxfeerate=0)
diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py
index 794b2e5e08..1e73dcf5cd 100755
--- a/test/functional/rpc_blockchain.py
+++ b/test/functional/rpc_blockchain.py
@@ -27,6 +27,7 @@ import subprocess
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.blocktools import (
+ DERSIG_HEIGHT,
create_block,
create_coinbase,
TIME_GENESIS_BLOCK,
@@ -140,8 +141,8 @@ class BlockchainTest(BitcoinTestFramework):
assert_greater_than(res['size_on_disk'], 0)
assert_equal(res['softforks'], {
- 'bip34': {'type': 'buried', 'active': False, 'height': 500},
- 'bip66': {'type': 'buried', 'active': False, 'height': 1251},
+ 'bip34': {'type': 'buried', 'active': True, 'height': 2},
+ 'bip66': {'type': 'buried', 'active': True, 'height': DERSIG_HEIGHT},
'bip65': {'type': 'buried', 'active': False, 'height': 1351},
'csv': {'type': 'buried', 'active': False, 'height': 432},
'segwit': {'type': 'buried', 'active': True, 'height': 0},
diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py
index 816ec67492..bb2e51c2f7 100755
--- a/test/functional/rpc_createmultisig.py
+++ b/test/functional/rpc_createmultisig.py
@@ -3,7 +3,6 @@
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test multisig RPCs"""
-import binascii
import decimal
import itertools
import json
@@ -66,9 +65,9 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
# decompress pk2
pk_obj = ECPubKey()
- pk_obj.set(binascii.unhexlify(pk2))
+ pk_obj.set(bytes.fromhex(pk2))
pk_obj.compressed = False
- pk2 = binascii.hexlify(pk_obj.get_bytes()).decode()
+ pk2 = pk_obj.get_bytes().hex()
node0.createwallet(wallet_name='wmulti0', disable_private_keys=True)
wmulti0 = node0.get_wallet_rpc('wmulti0')
diff --git a/test/functional/rpc_decodescript.py b/test/functional/rpc_decodescript.py
index f6643c7167..5b1514af6f 100755
--- a/test/functional/rpc_decodescript.py
+++ b/test/functional/rpc_decodescript.py
@@ -11,7 +11,6 @@ from test_framework.messages import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
- hex_str_to_bytes,
)
@@ -86,7 +85,7 @@ class DecodeScriptTest(BitcoinTestFramework):
rpc_result = self.nodes[0].decodescript(multisig_script)
assert_equal('2 ' + public_key + ' ' + public_key + ' ' + public_key + ' 3 OP_CHECKMULTISIG', rpc_result['asm'])
# multisig in P2WSH
- multisig_script_hash = sha256(hex_str_to_bytes(multisig_script)).hex()
+ multisig_script_hash = sha256(bytes.fromhex(multisig_script)).hex()
assert_equal('0 ' + multisig_script_hash, rpc_result['segwit']['asm'])
# 4) P2SH scriptPubKey
@@ -124,7 +123,7 @@ class DecodeScriptTest(BitcoinTestFramework):
rpc_result = self.nodes[0].decodescript(cltv_script)
assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm'])
# CLTV script in P2WSH
- cltv_script_hash = sha256(hex_str_to_bytes(cltv_script)).hex()
+ cltv_script_hash = sha256(bytes.fromhex(cltv_script)).hex()
assert_equal('0 ' + cltv_script_hash, rpc_result['segwit']['asm'])
# 7) P2PK scriptPubKey
@@ -209,23 +208,23 @@ class DecodeScriptTest(BitcoinTestFramework):
signature_2_sighash_decoded = der_signature + '[NONE|ANYONECANPAY]'
# 1) P2PK scriptSig
- txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature)
+ txSave.vin[0].scriptSig = bytes.fromhex(push_signature)
rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex())
assert_equal(signature_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm'])
# make sure that the sighash decodes come out correctly for a more complex / lesser used case.
- txSave.vin[0].scriptSig = hex_str_to_bytes(push_signature_2)
+ txSave.vin[0].scriptSig = bytes.fromhex(push_signature_2)
rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex())
assert_equal(signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm'])
# 2) multisig scriptSig
- txSave.vin[0].scriptSig = hex_str_to_bytes('00' + push_signature + push_signature_2)
+ txSave.vin[0].scriptSig = bytes.fromhex('00' + push_signature + push_signature_2)
rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex())
assert_equal('0 ' + signature_sighash_decoded + ' ' + signature_2_sighash_decoded, rpc_result['vin'][0]['scriptSig']['asm'])
# 3) test a scriptSig that contains more than push operations.
# in fact, it contains an OP_RETURN with data specially crafted to cause improper decode if the code does not catch it.
- txSave.vin[0].scriptSig = hex_str_to_bytes('6a143011020701010101010101020601010101010101')
+ txSave.vin[0].scriptSig = bytes.fromhex('6a143011020701010101010101020601010101010101')
rpc_result = self.nodes[0].decoderawtransaction(txSave.serialize().hex())
assert_equal('OP_RETURN 3011020701010101010101020601010101010101', rpc_result['vin'][0]['scriptSig']['asm'])
diff --git a/test/functional/rpc_packages.py b/test/functional/rpc_packages.py
index 4b2ed20958..3cb4154601 100755
--- a/test/functional/rpc_packages.py
+++ b/test/functional/rpc_packages.py
@@ -22,6 +22,11 @@ from test_framework.script import (
from test_framework.util import (
assert_equal,
)
+from test_framework.wallet import (
+ create_child_with_parents,
+ create_raw_chain,
+ make_chain,
+)
class RPCPackagesTest(BitcoinTestFramework):
def set_test_params(self):
@@ -78,26 +83,6 @@ class RPCPackagesTest(BitcoinTestFramework):
self.test_conflicting()
self.test_rbf()
- def chain_transaction(self, parent_txid, parent_value, n=0, parent_locking_script=None):
- """Build a transaction that spends parent_txid.vout[n] and produces one output with
- amount = parent_value with a fee deducted.
- Return tuple (CTransaction object, raw hex, nValue, scriptPubKey of the output created).
- """
- node = self.nodes[0]
- inputs = [{"txid": parent_txid, "vout": n}]
- my_value = parent_value - Decimal("0.0001")
- outputs = {self.address : my_value}
- rawtx = node.createrawtransaction(inputs, outputs)
- prevtxs = [{
- "txid": parent_txid,
- "vout": n,
- "scriptPubKey": parent_locking_script,
- "amount": parent_value,
- }] if parent_locking_script else None
- signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=self.privkeys, prevtxs=prevtxs)
- assert signedtx["complete"]
- tx = tx_from_hex(signedtx["hex"])
- return (tx, signedtx["hex"], my_value, tx.vout[0].scriptPubKey.hex())
def test_independent(self):
self.log.info("Test multiple independent transactions in a package")
@@ -148,20 +133,7 @@ class RPCPackagesTest(BitcoinTestFramework):
def test_chain(self):
node = self.nodes[0]
first_coin = self.coins.pop()
-
- # Chain of 25 transactions
- parent_locking_script = None
- txid = first_coin["txid"]
- chain_hex = []
- chain_txns = []
- value = first_coin["amount"]
-
- for _ in range(25):
- (tx, txhex, value, parent_locking_script) = self.chain_transaction(txid, value, 0, parent_locking_script)
- txid = tx.rehash()
- chain_hex.append(txhex)
- chain_txns.append(tx)
-
+ (chain_hex, chain_txns) = create_raw_chain(node, first_coin, self.address, self.privkeys)
self.log.info("Check that testmempoolaccept requires packages to be sorted by dependency")
assert_equal(node.testmempoolaccept(rawtxs=chain_hex[::-1]),
[{"txid": tx.rehash(), "wtxid": tx.getwtxid(), "package-error": "package-not-sorted"} for tx in chain_txns[::-1]])
@@ -201,7 +173,7 @@ class RPCPackagesTest(BitcoinTestFramework):
child_value = value - Decimal("0.0001")
# Child A
- (_, tx_child_a_hex, _, _) = self.chain_transaction(parent_txid, child_value, 0, parent_locking_script_a)
+ (_, tx_child_a_hex, _, _) = make_chain(node, self.address, self.privkeys, parent_txid, child_value, 0, parent_locking_script_a)
assert not node.testmempoolaccept([tx_child_a_hex])[0]["allowed"]
# Child B
@@ -226,19 +198,6 @@ class RPCPackagesTest(BitcoinTestFramework):
node.sendrawtransaction(rawtx)
assert_equal(testres_single, testres_multiple_ab)
- def create_child_with_parents(self, parents_tx, values, locking_scripts):
- """Creates a transaction that spends the first output of each parent in parents_tx."""
- num_parents = len(parents_tx)
- total_value = sum(values)
- inputs = [{"txid": tx.rehash(), "vout": 0} for tx in parents_tx]
- outputs = {self.address : total_value - num_parents * Decimal("0.0001")}
- rawtx_child = self.nodes[0].createrawtransaction(inputs, outputs)
- prevtxs = []
- for i in range(num_parents):
- prevtxs.append({"txid": parents_tx[i].rehash(), "vout": 0, "scriptPubKey": locking_scripts[i], "amount": values[i]})
- signedtx_child = self.nodes[0].signrawtransactionwithkey(hexstring=rawtx_child, privkeys=self.privkeys, prevtxs=prevtxs)
- assert signedtx_child["complete"]
- return signedtx_child["hex"]
def test_multiple_parents(self):
node = self.nodes[0]
@@ -253,12 +212,12 @@ class RPCPackagesTest(BitcoinTestFramework):
for _ in range(num_parents):
parent_coin = self.coins.pop()
value = parent_coin["amount"]
- (tx, txhex, value, parent_locking_script) = self.chain_transaction(parent_coin["txid"], value)
+ (tx, txhex, value, parent_locking_script) = make_chain(node, self.address, self.privkeys, parent_coin["txid"], value)
package_hex.append(txhex)
parents_tx.append(tx)
values.append(value)
parent_locking_scripts.append(parent_locking_script)
- child_hex = self.create_child_with_parents(parents_tx, values, parent_locking_scripts)
+ child_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, parent_locking_scripts)
# Package accept should work with the parents in any order (as long as parents come before child)
for _ in range(10):
random.shuffle(package_hex)
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 571029155e..312a4abbc3 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -20,7 +20,6 @@ from test_framework.util import (
assert_raises_rpc_error,
find_vout_for_address,
generate_to_height,
- hex_str_to_bytes,
)
from test_framework.messages import (
CTxInWitness,
@@ -233,7 +232,7 @@ class SignRawTransactionsTest(BitcoinTestFramework):
embedded_pubkey = eckey.get_pubkey().get_bytes().hex()
witness_script = {
'P2PKH': key_to_p2pkh_script(embedded_pubkey).hex(),
- 'P2PK': CScript([hex_str_to_bytes(embedded_pubkey), OP_CHECKSIG]).hex()
+ 'P2PK': CScript([bytes.fromhex(embedded_pubkey), OP_CHECKSIG]).hex()
}.get(tx_type, "Invalid tx_type")
redeem_script = script_to_p2wsh_script(witness_script).hex()
addr = script_to_p2sh(redeem_script)
diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py
index 360962b8da..fe733e9368 100644
--- a/test/functional/test_framework/address.py
+++ b/test/functional/test_framework/address.py
@@ -12,7 +12,7 @@ import unittest
from .script import hash256, hash160, sha256, CScript, OP_0
from .segwit_addr import encode_segwit_address
-from .util import assert_equal, hex_str_to_bytes
+from .util import assert_equal
ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR = 'addr(bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj)#juyq9d97'
@@ -33,7 +33,7 @@ def byte_to_base58(b, version):
result = ''
str = b.hex()
str = chr(version).encode('latin-1').hex() + str
- checksum = hash256(hex_str_to_bytes(str)).hex()
+ checksum = hash256(bytes.fromhex(str)).hex()
str += checksum[:8]
value = int('0x' + str, 0)
while value > 0:
@@ -100,7 +100,7 @@ def key_to_p2sh_p2wpkh(key, main=False):
def program_to_witness(version, program, main=False):
if (type(program) is str):
- program = hex_str_to_bytes(program)
+ program = bytes.fromhex(program)
assert 0 <= version <= 16
assert 2 <= len(program) <= 40
assert version > 0 or len(program) in [20, 32]
@@ -121,14 +121,14 @@ def script_to_p2sh_p2wsh(script, main=False):
def check_key(key):
if (type(key) is str):
- key = hex_str_to_bytes(key) # Assuming this is hex string
+ key = bytes.fromhex(key) # Assuming this is hex string
if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
return key
assert False
def check_script(script):
if (type(script) is str):
- script = hex_str_to_bytes(script) # Assuming this is hex string
+ script = bytes.fromhex(script) # Assuming this is hex string
if (type(script) is bytes or type(script) is CScript):
return script
assert False
diff --git a/test/functional/test_framework/bdb.py b/test/functional/test_framework/bdb.py
index 97b9c1d6d0..d623bcdf6e 100644
--- a/test/functional/test_framework/bdb.py
+++ b/test/functional/test_framework/bdb.py
@@ -24,7 +24,6 @@ transactions.
`db_dump -da wallet.dat` is useful to see the data in a wallet.dat BDB file
"""
-import binascii
import struct
# Important constants
@@ -96,7 +95,7 @@ def dump_meta_page(page):
metadata['key_count'] = key_count
metadata['record_count'] = record_count
metadata['flags'] = flags
- metadata['uid'] = binascii.hexlify(uid)
+ metadata['uid'] = uid.hex().encode()
assert magic == BTREE_MAGIC, 'bdb magic does not match bdb btree magic'
assert pg_type == BTREE_META, 'Metadata page is not a btree metadata page'
@@ -110,8 +109,9 @@ def dump_meta_page(page):
metadata['re_pad'] = re_pad
metadata['root'] = root
metadata['crypto_magic'] = crypto_magic
- metadata['iv'] = binascii.hexlify(iv)
- metadata['chksum'] = binascii.hexlify(chksum)
+ metadata['iv'] = iv.hex().encode()
+ metadata['chksum'] = chksum.hex().encode()
+
return metadata
# Given the dict from dump_leaf_page, get the key-value pairs and put them into a dict
diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py
index 2ab720aafb..11d0ab40d5 100644
--- a/test/functional/test_framework/blocktools.py
+++ b/test/functional/test_framework/blocktools.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Utilities for manipulating blocks and transactions."""
-from binascii import a2b_hex
import struct
import time
import unittest
@@ -24,7 +23,6 @@ from .messages import (
CTxInWitness,
CTxOut,
hash256,
- hex_str_to_bytes,
ser_uint256,
tx_from_hex,
uint256_from_str,
@@ -56,6 +54,7 @@ TIME_GENESIS_BLOCK = 1296688602
COINBASE_MATURITY = 100
# Soft-fork activation heights
+DERSIG_HEIGHT = 102 # BIP 66
CLTV_HEIGHT = 1351
CSV_ACTIVATION_HEIGHT = 432
@@ -63,6 +62,7 @@ CSV_ACTIVATION_HEIGHT = 432
WITNESS_COMMITMENT_HEADER = b"\xaa\x21\xa9\xed"
NORMAL_GBT_REQUEST_PARAMS = {"rules": ["segwit"]}
+VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4
def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl=None, txlist=None):
@@ -70,11 +70,11 @@ def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl
block = CBlock()
if tmpl is None:
tmpl = {}
- block.nVersion = version or tmpl.get('version') or 1
+ block.nVersion = version or tmpl.get('version') or VERSIONBITS_LAST_OLD_BLOCK_VERSION
block.nTime = ntime or tmpl.get('curtime') or int(time.time() + 600)
block.hashPrevBlock = hashprev or int(tmpl['previousblockhash'], 0x10)
if tmpl and not tmpl.get('bits') is None:
- block.nBits = struct.unpack('>I', a2b_hex(tmpl['bits']))[0]
+ block.nBits = struct.unpack('>I', bytes.fromhex(tmpl['bits']))[0]
else:
block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams
if coinbase is None:
@@ -214,8 +214,8 @@ def witness_script(use_p2wsh, pubkey):
pkscript = key_to_p2wpkh_script(pubkey)
else:
# 1-of-1 multisig
- witness_program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
- pkscript = script_to_p2wsh_script(witness_program)
+ witness_script = CScript([OP_1, bytes.fromhex(pubkey), OP_1, OP_CHECKMULTISIG])
+ pkscript = script_to_p2wsh_script(witness_script)
return pkscript.hex()
def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
@@ -223,7 +223,7 @@ def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount):
Optionally wrap the segwit output using P2SH."""
if use_p2wsh:
- program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG])
+ program = CScript([OP_1, bytes.fromhex(pubkey), OP_1, OP_CHECKMULTISIG])
addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program)
else:
addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey)
@@ -246,7 +246,7 @@ def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=Tru
else:
if (insert_redeem_script):
tx = tx_from_hex(tx_to_witness)
- tx.vin[0].scriptSig += CScript([hex_str_to_bytes(insert_redeem_script)])
+ tx.vin[0].scriptSig += CScript([bytes.fromhex(insert_redeem_script)])
tx_to_witness = tx.serialize().hex()
return node.sendrawtransaction(tx_to_witness)
diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py
index 065e8961ae..6e57107f86 100755
--- a/test/functional/test_framework/messages.py
+++ b/test/functional/test_framework/messages.py
@@ -30,10 +30,10 @@ import struct
import time
from test_framework.siphash import siphash256
-from test_framework.util import hex_str_to_bytes, assert_equal
+from test_framework.util import assert_equal
MAX_LOCATOR_SZ = 101
-MAX_BLOCK_BASE_SIZE = 1000000
+MAX_BLOCK_WEIGHT = 4000000
MAX_BLOOM_FILTER_SIZE = 36000
MAX_BLOOM_HASH_FUNCS = 50
@@ -197,7 +197,7 @@ def from_hex(obj, hex_string):
Note that there is no complementary helper like e.g. `to_hex` for the
inverse operation. To serialize a message object to a hex string, simply
use obj.serialize().hex()"""
- obj.deserialize(BytesIO(hex_str_to_bytes(hex_string)))
+ obj.deserialize(BytesIO(bytes.fromhex(hex_string)))
return obj
@@ -608,12 +608,15 @@ class CTransaction:
return False
return True
- # Calculate the virtual transaction size using witness and non-witness
+ # Calculate the transaction weight using witness and non-witness
# serialization size (does NOT use sigops).
- def get_vsize(self):
+ def get_weight(self):
with_witness_size = len(self.serialize_with_witness())
without_witness_size = len(self.serialize_without_witness())
- return math.ceil(((WITNESS_SCALE_FACTOR - 1) * without_witness_size + with_witness_size) / WITNESS_SCALE_FACTOR)
+ return (WITNESS_SCALE_FACTOR - 1) * without_witness_size + with_witness_size
+
+ def get_vsize(self):
+ return math.ceil(self.get_weight() / WITNESS_SCALE_FACTOR)
def __repr__(self):
return "CTransaction(nVersion=%i vin=%s vout=%s wit=%s nLockTime=%i)" \
@@ -639,7 +642,7 @@ class CBlockHeader:
self.calc_sha256()
def set_null(self):
- self.nVersion = 1
+ self.nVersion = 4
self.hashPrevBlock = 0
self.hashMerkleRoot = 0
self.nTime = 0
@@ -761,6 +764,13 @@ class CBlock(CBlockHeader):
self.nNonce += 1
self.rehash()
+ # Calculate the block weight using witness and non-witness
+ # serialization size (does NOT use sigops).
+ def get_weight(self):
+ with_witness_size = len(self.serialize(with_witness=True))
+ without_witness_size = len(self.serialize(with_witness=False))
+ return (WITNESS_SCALE_FACTOR - 1) * without_witness_size + with_witness_size
+
def __repr__(self):
return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \
% (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot,
diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py
index 5dc723c1d5..b5f78e0cf3 100644
--- a/test/functional/test_framework/netutil.py
+++ b/test/functional/test_framework/netutil.py
@@ -12,7 +12,6 @@ import socket
import struct
import array
import os
-from binascii import unhexlify
# STATE_ESTABLISHED = '01'
# STATE_SYN_SENT = '02'
@@ -44,7 +43,7 @@ def _remove_empty(array):
def _convert_ip_port(array):
host,port = array.split(':')
# convert host from mangled-per-four-bytes form as used by kernel
- host = unhexlify(host)
+ host = bytes.fromhex(host)
host_out = ''
for x in range(0, len(host) // 4):
(val,) = struct.unpack('=I', host[x*4:(x+1)*4])
diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py
index cc80b543cd..b7d5bd8fab 100755
--- a/test/functional/test_framework/p2p.py
+++ b/test/functional/test_framework/p2p.py
@@ -438,6 +438,7 @@ class P2PInterface(P2PConnection):
self.send_message(msg_sendaddrv2())
self.send_message(msg_verack())
self.nServices = message.nServices
+ self.send_message(msg_getaddr())
# Connection helper methods
diff --git a/test/functional/test_framework/script_util.py b/test/functional/test_framework/script_util.py
index 457be6b0e6..5d1d7ea45c 100755
--- a/test/functional/test_framework/script_util.py
+++ b/test/functional/test_framework/script_util.py
@@ -4,7 +4,6 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Useful Script constants and utils."""
from test_framework.script import CScript, hash160, sha256, OP_0, OP_DUP, OP_HASH160, OP_CHECKSIG, OP_EQUAL, OP_EQUALVERIFY
-from test_framework.util import hex_str_to_bytes
# To prevent a "tx-size-small" policy rule error, a transaction has to have a
# non-witness size of at least 82 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in
@@ -49,7 +48,7 @@ def key_to_p2sh_p2wpkh_script(key, main = False):
def program_to_witness_script(version, program, main = False):
if isinstance(program, str):
- program = hex_str_to_bytes(program)
+ program = bytes.fromhex(program)
assert 0 <= version <= 16
assert 2 <= len(program) <= 40
assert version > 0 or len(program) in [20, 32]
@@ -70,14 +69,14 @@ def script_to_p2sh_p2wsh_script(script, main = False):
def check_key(key):
if isinstance(key, str):
- key = hex_str_to_bytes(key) # Assuming this is hex string
+ key = bytes.fromhex(key) # Assuming this is hex string
if isinstance(key, bytes) and (len(key) == 33 or len(key) == 65):
return key
assert False
def check_script(script):
if isinstance(script, str):
- script = hex_str_to_bytes(script) # Assuming this is hex string
+ script = bytes.fromhex(script) # Assuming this is hex string
if isinstance(script, bytes) or isinstance(script, CScript):
return script
assert False
diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py
index a9a6adcfc8..54f2fdee21 100644
--- a/test/functional/test_framework/util.py
+++ b/test/functional/test_framework/util.py
@@ -5,7 +5,6 @@
"""Helpful routines for regression testing."""
from base64 import b64encode
-from binascii import unhexlify
from decimal import Decimal, ROUND_DOWN
from subprocess import CalledProcessError
import hashlib
@@ -214,10 +213,6 @@ def count_bytes(hex_string):
return len(bytearray.fromhex(hex_string))
-def hex_str_to_bytes(hex_str):
- return unhexlify(hex_str.encode('ascii'))
-
-
def str_to_b64str(string):
return b64encode(string.encode('utf-8')).decode('ascii')
@@ -517,7 +512,7 @@ def gen_return_txouts():
from .messages import CTxOut
txout = CTxOut()
txout.nValue = 0
- txout.scriptPubKey = hex_str_to_bytes(script_pubkey)
+ txout.scriptPubKey = bytes.fromhex(script_pubkey)
for _ in range(128):
txouts.append(txout)
return txouts
diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py
index 47ec6b0be2..ba5b95f930 100644
--- a/test/functional/test_framework/wallet.py
+++ b/test/functional/test_framework/wallet.py
@@ -4,8 +4,10 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""A limited-functionality wallet, which may replace a real wallet in tests"""
+from copy import deepcopy
from decimal import Decimal
from enum import Enum
+from random import choice
from typing import Optional
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
from test_framework.key import ECKey
@@ -16,6 +18,7 @@ from test_framework.messages import (
CTxIn,
CTxInWitness,
CTxOut,
+ tx_from_hex,
)
from test_framework.script import (
CScript,
@@ -27,10 +30,11 @@ from test_framework.script import (
)
from test_framework.util import (
assert_equal,
- hex_str_to_bytes,
+ assert_greater_than_or_equal,
satoshi_round,
)
+DEFAULT_FEE = Decimal("0.0001")
class MiniWalletMode(Enum):
"""Determines the transaction type the MiniWallet is creating and spending.
@@ -73,7 +77,7 @@ class MiniWallet:
self._scriptPubKey = bytes(CScript([pub_key.get_bytes(), OP_CHECKSIG]))
elif mode == MiniWalletMode.ADDRESS_OP_TRUE:
self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
- self._scriptPubKey = hex_str_to_bytes(self._test_node.validateaddress(self._address)['scriptPubKey'])
+ self._scriptPubKey = bytes.fromhex(self._test_node.validateaddress(self._address)['scriptPubKey'])
def scan_blocks(self, *, start=1, num):
"""Scan the blocks for self._address outputs and add them to self._utxos"""
@@ -177,3 +181,75 @@ class MiniWallet:
def sendrawtransaction(self, *, from_node, tx_hex):
from_node.sendrawtransaction(tx_hex)
self.scan_tx(from_node.decoderawtransaction(tx_hex))
+
+def make_chain(node, address, privkeys, parent_txid, parent_value, n=0, parent_locking_script=None, fee=DEFAULT_FEE):
+ """Build a transaction that spends parent_txid.vout[n] and produces one output with
+ amount = parent_value with a fee deducted.
+ Return tuple (CTransaction object, raw hex, nValue, scriptPubKey of the output created).
+ """
+ inputs = [{"txid": parent_txid, "vout": n}]
+ my_value = parent_value - fee
+ outputs = {address : my_value}
+ rawtx = node.createrawtransaction(inputs, outputs)
+ prevtxs = [{
+ "txid": parent_txid,
+ "vout": n,
+ "scriptPubKey": parent_locking_script,
+ "amount": parent_value,
+ }] if parent_locking_script else None
+ signedtx = node.signrawtransactionwithkey(hexstring=rawtx, privkeys=privkeys, prevtxs=prevtxs)
+ assert signedtx["complete"]
+ tx = tx_from_hex(signedtx["hex"])
+ return (tx, signedtx["hex"], my_value, tx.vout[0].scriptPubKey.hex())
+
+def create_child_with_parents(node, address, privkeys, parents_tx, values, locking_scripts, fee=DEFAULT_FEE):
+ """Creates a transaction that spends the first output of each parent in parents_tx."""
+ num_parents = len(parents_tx)
+ total_value = sum(values)
+ inputs = [{"txid": tx.rehash(), "vout": 0} for tx in parents_tx]
+ outputs = {address : total_value - fee}
+ rawtx_child = node.createrawtransaction(inputs, outputs)
+ prevtxs = []
+ for i in range(num_parents):
+ prevtxs.append({"txid": parents_tx[i].rehash(), "vout": 0, "scriptPubKey": locking_scripts[i], "amount": values[i]})
+ signedtx_child = node.signrawtransactionwithkey(hexstring=rawtx_child, privkeys=privkeys, prevtxs=prevtxs)
+ assert signedtx_child["complete"]
+ return signedtx_child["hex"]
+
+def create_raw_chain(node, first_coin, address, privkeys, chain_length=25):
+ """Helper function: create a "chain" of chain_length transactions. The nth transaction in the
+ chain is a child of the n-1th transaction and parent of the n+1th transaction.
+ """
+ parent_locking_script = None
+ txid = first_coin["txid"]
+ chain_hex = []
+ chain_txns = []
+ value = first_coin["amount"]
+
+ for _ in range(chain_length):
+ (tx, txhex, value, parent_locking_script) = make_chain(node, address, privkeys, txid, value, 0, parent_locking_script)
+ txid = tx.rehash()
+ chain_hex.append(txhex)
+ chain_txns.append(tx)
+
+ return (chain_hex, chain_txns)
+
+def bulk_transaction(tx, node, target_weight, privkeys, prevtxs=None):
+ """Pad a transaction with extra outputs until it reaches a target weight (or higher).
+ returns CTransaction object
+ """
+ tx_heavy = deepcopy(tx)
+ assert_greater_than_or_equal(target_weight, tx_heavy.get_weight())
+ while tx_heavy.get_weight() < target_weight:
+ random_spk = "6a4d0200" # OP_RETURN OP_PUSH2 512 bytes
+ for _ in range(512*2):
+ random_spk += choice("0123456789ABCDEF")
+ tx_heavy.vout.append(CTxOut(0, bytes.fromhex(random_spk)))
+ # Re-sign the transaction
+ if privkeys:
+ signed = node.signrawtransactionwithkey(tx_heavy.serialize().hex(), privkeys, prevtxs)
+ return tx_from_hex(signed["hex"])
+ # OP_TRUE
+ tx_heavy.wit.vtxinwit = [CTxInWitness()]
+ tx_heavy.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
+ return tx_heavy
diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py
index acbc040741..1ee55aa3b7 100755
--- a/test/functional/test_framework/wallet_util.py
+++ b/test/functional/test_framework/wallet_util.py
@@ -27,7 +27,6 @@ from test_framework.script_util import (
script_to_p2sh_script,
script_to_p2wsh_script,
)
-from test_framework.util import hex_str_to_bytes
Key = namedtuple('Key', ['privkey',
'pubkey',
@@ -93,7 +92,7 @@ def get_multisig(node):
addr = node.getaddressinfo(node.getnewaddress())
addrs.append(addr['address'])
pubkeys.append(addr['pubkey'])
- script_code = CScript([OP_2] + [hex_str_to_bytes(pubkey) for pubkey in pubkeys] + [OP_3, OP_CHECKMULTISIG])
+ script_code = CScript([OP_2] + [bytes.fromhex(pubkey) for pubkey in pubkeys] + [OP_3, OP_CHECKMULTISIG])
witness_script = script_to_p2wsh_script(script_code)
return Multisig(privkeys=[node.dumpprivkey(addr) for addr in addrs],
pubkeys=pubkeys,
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 0a73891f2a..1a1a6a263a 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -121,6 +121,7 @@ BASE_SCRIPTS = [
'wallet_listreceivedby.py --legacy-wallet',
'wallet_listreceivedby.py --descriptors',
'wallet_abandonconflict.py --legacy-wallet',
+ 'p2p_dns_seeds.py',
'wallet_abandonconflict.py --descriptors',
'feature_csv_activation.py',
'wallet_address_types.py --legacy-wallet',
@@ -217,6 +218,7 @@ BASE_SCRIPTS = [
'rpc_createmultisig.py --legacy-wallet',
'rpc_createmultisig.py --descriptors',
'rpc_packages.py',
+ 'mempool_package_limits.py',
'feature_versionbits_warning.py',
'rpc_preciousblock.py',
'wallet_importprunedfunds.py --legacy-wallet',
diff --git a/test/functional/wallet_listdescriptors.py b/test/functional/wallet_listdescriptors.py
index c2565d84f6..221f5262d9 100755
--- a/test/functional/wallet_listdescriptors.py
+++ b/test/functional/wallet_listdescriptors.py
@@ -72,11 +72,39 @@ class ListDescriptorsTest(BitcoinTestFramework):
],
}
assert_equal(expected, wallet.listdescriptors())
+ assert_equal(expected, wallet.listdescriptors(False))
+
+ self.log.info('Test list private descriptors')
+ expected_private = {
+ 'wallet_name': 'w2',
+ 'descriptors': [
+ {'desc': descsum_create('wpkh(' + xprv + hardened_path + '/0/*)'),
+ 'timestamp': 1296688602,
+ 'active': False,
+ 'range': [0, 0],
+ 'next': 0},
+ ],
+ }
+ assert_equal(expected_private, wallet.listdescriptors(True))
self.log.info("Test listdescriptors with encrypted wallet")
wallet.encryptwallet("pass")
assert_equal(expected, wallet.listdescriptors())
+ self.log.info('Test list private descriptors with encrypted wallet')
+ assert_raises_rpc_error(-13, 'Please enter the wallet passphrase with walletpassphrase first.', wallet.listdescriptors, True)
+ wallet.walletpassphrase(passphrase="pass", timeout=1000000)
+ assert_equal(expected_private, wallet.listdescriptors(True))
+
+ self.log.info('Test list private descriptors with watch-only wallet')
+ node.createwallet(wallet_name='watch-only', descriptors=True, disable_private_keys=True)
+ watch_only_wallet = node.get_wallet_rpc('watch-only')
+ watch_only_wallet.importdescriptors([{
+ 'desc': descsum_create('wpkh(' + xpub_acc + ')'),
+ 'timestamp': 1296688602,
+ }])
+ assert_raises_rpc_error(-4, 'Can\'t get descriptor string', watch_only_wallet.listdescriptors, True)
+
self.log.info('Test non-active non-range combo descriptor')
node.createwallet(wallet_name='w4', blank=True, descriptors=True)
wallet = node.get_wallet_rpc('w4')