aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/denialofservice_tests.cpp15
-rw-r--r--src/test/fuzz/addrman.cpp119
-rw-r--r--src/test/fuzz/coins_view.cpp3
-rw-r--r--src/test/fuzz/connman.cpp162
-rw-r--r--src/test/fuzz/decode_tx.cpp7
-rw-r--r--src/test/fuzz/descriptor_parse.cpp3
-rw-r--r--src/test/fuzz/deserialize.cpp57
-rw-r--r--src/test/fuzz/fuzz.cpp9
-rw-r--r--src/test/fuzz/merkleblock.cpp34
-rw-r--r--src/test/fuzz/net.cpp6
-rw-r--r--src/test/fuzz/process_message.cpp7
-rw-r--r--src/test/fuzz/process_messages.cpp6
-rw-r--r--src/test/fuzz/script_assets_test_minimizer.cpp200
-rw-r--r--src/test/fuzz/script_flags.cpp8
-rw-r--r--src/test/fuzz/script_sigcache.cpp18
-rw-r--r--src/test/fuzz/signature_checker.cpp7
-rw-r--r--src/test/fuzz/transaction.cpp3
-rw-r--r--src/test/fuzz/txrequest.cpp374
-rw-r--r--src/test/fuzz/util.h41
-rw-r--r--src/test/key_tests.cpp28
-rw-r--r--src/test/limitedmap_tests.cpp101
-rw-r--r--src/test/miner_tests.cpp2
-rw-r--r--src/test/net_tests.cpp81
-rw-r--r--src/test/netbase_tests.cpp105
-rw-r--r--src/test/pow_tests.cpp32
-rw-r--r--src/test/script_p2sh_tests.cpp6
-rw-r--r--src/test/script_tests.cpp157
-rw-r--r--src/test/transaction_tests.cpp3
-rw-r--r--src/test/txrequest_tests.cpp738
-rw-r--r--src/test/txvalidation_tests.cpp3
-rw-r--r--src/test/txvalidationcache_tests.cpp2
-rw-r--r--src/test/util/validation.cpp22
-rw-r--r--src/test/util/validation.h17
-rw-r--r--src/test/util_tests.cpp22
-rw-r--r--src/test/validation_block_tests.cpp3
-rw-r--r--src/test/validation_tests.cpp79
-rw-r--r--src/test/versionbits_tests.cpp4
37 files changed, 2267 insertions, 217 deletions
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 712567ac0d..c399da900f 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -88,7 +88,6 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
- dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
// This test requires that we have a chain with non-zero work.
@@ -130,7 +129,7 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction)
SetMockTime(0);
bool dummy;
- peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
+ peerLogic->FinalizeNode(dummyNode1, dummy);
}
static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &peerLogic, CConnmanTest* connman)
@@ -141,7 +140,6 @@ static void AddRandomOutboundPeer(std::vector<CNode *> &vNodes, PeerManager &pee
node.SetCommonVersion(PROTOCOL_VERSION);
peerLogic.InitializeNode(&node);
- node.nVersion = 1;
node.fSuccessfullyConnected = true;
connman->AddNode(node);
@@ -213,7 +211,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management)
bool dummy;
for (const CNode *node : vNodes) {
- peerLogic->FinalizeNode(node->GetId(), dummy);
+ peerLogic->FinalizeNode(*node, dummy);
}
connman->ClearNodes();
@@ -231,7 +229,6 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", ConnectionType::INBOUND);
dummyNode1.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode1);
- dummyNode1.nVersion = 1;
dummyNode1.fSuccessfullyConnected = true;
peerLogic->Misbehaving(dummyNode1.GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ ""); // Should be discouraged
{
@@ -245,7 +242,6 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", ConnectionType::INBOUND);
dummyNode2.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode2);
- dummyNode2.nVersion = 1;
dummyNode2.fSuccessfullyConnected = true;
peerLogic->Misbehaving(dummyNode2.GetId(), DISCOURAGEMENT_THRESHOLD - 1, /* message */ "");
{
@@ -263,8 +259,8 @@ BOOST_AUTO_TEST_CASE(peer_discouragement)
BOOST_CHECK(banman->IsDiscouraged(addr2)); // to be discouraged now
bool dummy;
- peerLogic->FinalizeNode(dummyNode1.GetId(), dummy);
- peerLogic->FinalizeNode(dummyNode2.GetId(), dummy);
+ peerLogic->FinalizeNode(dummyNode1, dummy);
+ peerLogic->FinalizeNode(dummyNode2, dummy);
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
@@ -282,7 +278,6 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, CAddress(), "", ConnectionType::INBOUND);
dummyNode.SetCommonVersion(PROTOCOL_VERSION);
peerLogic->InitializeNode(&dummyNode);
- dummyNode.nVersion = 1;
dummyNode.fSuccessfullyConnected = true;
peerLogic->Misbehaving(dummyNode.GetId(), DISCOURAGEMENT_THRESHOLD, /* message */ "");
@@ -293,7 +288,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
BOOST_CHECK(banman->IsDiscouraged(addr));
bool dummy;
- peerLogic->FinalizeNode(dummyNode.GetId(), dummy);
+ peerLogic->FinalizeNode(dummyNode, dummy);
}
static CTransactionRef RandomOrphan()
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
new file mode 100644
index 0000000000..0ceeea2d36
--- /dev/null
+++ b/src/test/fuzz/addrman.cpp
@@ -0,0 +1,119 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <addrdb.h>
+#include <addrman.h>
+#include <chainparams.h>
+#include <merkleblock.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <time.h>
+#include <util/asmap.h>
+
+#include <cstdint>
+#include <optional>
+#include <string>
+#include <vector>
+
+void initialize()
+{
+ SelectParams(CBaseChainParams::REGTEST);
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ SetMockTime(ConsumeTime(fuzzed_data_provider));
+ CAddrMan addr_man;
+ if (fuzzed_data_provider.ConsumeBool()) {
+ addr_man.m_asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
+ if (!SanityCheckASMap(addr_man.m_asmap)) {
+ addr_man.m_asmap.clear();
+ }
+ }
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) {
+ case 0: {
+ addr_man.Clear();
+ break;
+ }
+ case 1: {
+ addr_man.ResolveCollisions();
+ break;
+ }
+ case 2: {
+ (void)addr_man.SelectTriedCollision();
+ break;
+ }
+ case 3: {
+ (void)addr_man.Select(fuzzed_data_provider.ConsumeBool());
+ break;
+ }
+ case 4: {
+ (void)addr_man.GetAddr(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ break;
+ }
+ case 5: {
+ const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
+ if (opt_address && opt_net_addr) {
+ addr_man.Add(*opt_address, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000));
+ }
+ break;
+ }
+ case 6: {
+ std::vector<CAddress> addresses;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
+ if (!opt_address) {
+ break;
+ }
+ addresses.push_back(*opt_address);
+ }
+ const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
+ if (opt_net_addr) {
+ addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000));
+ }
+ break;
+ }
+ case 7: {
+ const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
+ if (opt_service) {
+ addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider));
+ }
+ break;
+ }
+ case 8: {
+ const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
+ if (opt_service) {
+ addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider));
+ }
+ break;
+ }
+ case 9: {
+ const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
+ if (opt_service) {
+ addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider));
+ }
+ break;
+ }
+ case 10: {
+ const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
+ if (opt_service) {
+ addr_man.SetServices(*opt_service, ServiceFlags{fuzzed_data_provider.ConsumeIntegral<uint64_t>()});
+ }
+ break;
+ }
+ case 11: {
+ (void)addr_man.Check();
+ break;
+ }
+ }
+ }
+ (void)addr_man.size();
+ CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION);
+ data_stream << addr_man;
+}
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index c186bef7ae..ac034809b0 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -229,7 +229,8 @@ void test_one_input(const std::vector<uint8_t>& buffer)
break;
}
case 1: {
- (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache);
+ (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, false);
+ (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache, true);
break;
}
case 2: {
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
new file mode 100644
index 0000000000..6521c3f3b2
--- /dev/null
+++ b/src/test/fuzz/connman.cpp
@@ -0,0 +1,162 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <chainparamsbase.h>
+#include <net.h>
+#include <netaddress.h>
+#include <protocol.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <util/translation.h>
+
+#include <cstdint>
+#include <vector>
+
+void initialize()
+{
+ InitializeFuzzingContext();
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ CConnman connman{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeBool()};
+ CAddress random_address;
+ CNetAddr random_netaddr;
+ CNode random_node = ConsumeNode(fuzzed_data_provider);
+ CService random_service;
+ CSubNet random_subnet;
+ std::string random_string;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 30)) {
+ case 0:
+ random_address = ConsumeAddress(fuzzed_data_provider);
+ break;
+ case 1:
+ random_netaddr = ConsumeNetAddr(fuzzed_data_provider);
+ break;
+ case 2:
+ random_service = ConsumeService(fuzzed_data_provider);
+ break;
+ case 3:
+ random_subnet = ConsumeSubNet(fuzzed_data_provider);
+ break;
+ case 4:
+ random_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
+ break;
+ case 5: {
+ std::vector<CAddress> addresses;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ addresses.push_back(ConsumeAddress(fuzzed_data_provider));
+ }
+ // Limit nTimePenalty to int32_t to avoid signed integer overflow
+ (void)connman.AddNewAddresses(addresses, ConsumeAddress(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int32_t>());
+ break;
+ }
+ case 6:
+ connman.AddNode(random_string);
+ break;
+ case 7:
+ connman.CheckIncomingNonce(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ break;
+ case 8:
+ connman.DisconnectNode(fuzzed_data_provider.ConsumeIntegral<NodeId>());
+ break;
+ case 9:
+ connman.DisconnectNode(random_netaddr);
+ break;
+ case 10:
+ connman.DisconnectNode(random_string);
+ break;
+ case 11:
+ connman.DisconnectNode(random_subnet);
+ break;
+ case 12:
+ connman.ForEachNode([](auto) {});
+ break;
+ case 13:
+ connman.ForEachNodeThen([](auto) {}, []() {});
+ break;
+ case 14:
+ (void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); });
+ break;
+ case 15:
+ (void)connman.GetAddresses(fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>());
+ break;
+ case 16: {
+ (void)connman.GetAddresses(random_node, fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>());
+ break;
+ }
+ case 17:
+ (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ break;
+ case 18:
+ (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL}));
+ break;
+ case 19:
+ connman.MarkAddressGood(random_address);
+ break;
+ case 20:
+ (void)connman.OutboundTargetReached(fuzzed_data_provider.ConsumeBool());
+ break;
+ case 21:
+ // Limit now to int32_t to avoid signed integer overflow
+ (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral<int32_t>(), fuzzed_data_provider.ConsumeIntegral<int>());
+ break;
+ case 22: {
+ CSerializedNetMsg serialized_net_msg;
+ serialized_net_msg.m_type = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::COMMAND_SIZE);
+ serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ connman.PushMessage(&random_node, std::move(serialized_net_msg));
+ break;
+ }
+ case 23:
+ connman.RemoveAddedNode(random_string);
+ break;
+ case 24: {
+ const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
+ if (SanityCheckASMap(asmap)) {
+ connman.SetAsmap(asmap);
+ }
+ break;
+ }
+ case 25:
+ connman.SetBestHeight(fuzzed_data_provider.ConsumeIntegral<int>());
+ break;
+ case 26:
+ connman.SetMaxOutboundTarget(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ break;
+ case 27:
+ connman.SetMaxOutboundTimeframe(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ break;
+ case 28:
+ connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool());
+ break;
+ case 29:
+ connman.SetServices(random_service, static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()));
+ break;
+ case 30:
+ connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool());
+ break;
+ }
+ }
+ (void)connman.GetAddedNodeInfo();
+ (void)connman.GetBestHeight();
+ (void)connman.GetExtraOutboundCount();
+ (void)connman.GetLocalServices();
+ (void)connman.GetMaxOutboundTarget();
+ (void)connman.GetMaxOutboundTimeframe();
+ (void)connman.GetMaxOutboundTimeLeftInCycle();
+ (void)connman.GetNetworkActive();
+ std::vector<CNodeStats> stats;
+ connman.GetNodeStats(stats);
+ (void)connman.GetOutboundTargetBytesLeft();
+ (void)connman.GetReceiveFloodSize();
+ (void)connman.GetTotalBytesRecv();
+ (void)connman.GetTotalBytesSent();
+ (void)connman.GetTryNewOutboundPeer();
+ (void)connman.GetUseAddrmanOutgoing();
+}
diff --git a/src/test/fuzz/decode_tx.cpp b/src/test/fuzz/decode_tx.cpp
index 0d89d4228a..a2b18c0365 100644
--- a/src/test/fuzz/decode_tx.cpp
+++ b/src/test/fuzz/decode_tx.cpp
@@ -19,13 +19,14 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const bool result_none = DecodeHexTx(mtx, tx_hex, false, false);
const bool result_try_witness = DecodeHexTx(mtx, tx_hex, false, true);
const bool result_try_witness_and_maybe_no_witness = DecodeHexTx(mtx, tx_hex, true, true);
- const bool result_try_no_witness = DecodeHexTx(mtx, tx_hex, true, false);
+ CMutableTransaction no_witness_mtx;
+ const bool result_try_no_witness = DecodeHexTx(no_witness_mtx, tx_hex, true, false);
assert(!result_none);
if (result_try_witness_and_maybe_no_witness) {
assert(result_try_no_witness || result_try_witness);
}
- // if (result_try_no_witness) { // Uncomment when https://github.com/bitcoin/bitcoin/pull/17775 is merged
- if (result_try_witness) { // Remove stop-gap when https://github.com/bitcoin/bitcoin/pull/17775 is merged
+ if (result_try_no_witness) {
+ assert(!no_witness_mtx.HasWitness());
assert(result_try_witness_and_maybe_no_witness);
}
}
diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp
index 001758ffdb..7b57a2c1e2 100644
--- a/src/test/fuzz/descriptor_parse.cpp
+++ b/src/test/fuzz/descriptor_parse.cpp
@@ -11,7 +11,8 @@
void initialize()
{
static const ECCVerifyHandle verify_handle;
- SelectParams(CBaseChainParams::REGTEST);
+ ECC_Start();
+ SelectParams(CBaseChainParams::MAIN);
}
void test_one_input(const std::vector<uint8_t>& buffer)
diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp
index b799d3b43b..8ca5366c8a 100644
--- a/src/test/fuzz/deserialize.cpp
+++ b/src/test/fuzz/deserialize.cpp
@@ -13,7 +13,9 @@
#include <key.h>
#include <merkleblock.h>
#include <net.h>
+#include <netbase.h>
#include <node/utxo_snapshot.h>
+#include <optional.h>
#include <primitives/block.h>
#include <protocol.h>
#include <psbt.h>
@@ -44,9 +46,9 @@ struct invalid_fuzzing_input_exception : public std::exception {
};
template <typename T>
-CDataStream Serialize(const T& obj)
+CDataStream Serialize(const T& obj, const int version = INIT_PROTO_VERSION)
{
- CDataStream ds(SER_NETWORK, INIT_PROTO_VERSION);
+ CDataStream ds(SER_NETWORK, version);
ds << obj;
return ds;
}
@@ -60,15 +62,19 @@ T Deserialize(CDataStream ds)
}
template <typename T>
-void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj)
+void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj, const Optional<int> protocol_version = nullopt)
{
CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION);
- try {
- int version;
- ds >> version;
- ds.SetVersion(version);
- } catch (const std::ios_base::failure&) {
- throw invalid_fuzzing_input_exception();
+ if (protocol_version) {
+ ds.SetVersion(*protocol_version);
+ } else {
+ try {
+ int version;
+ ds >> version;
+ ds.SetVersion(version);
+ } catch (const std::ios_base::failure&) {
+ throw invalid_fuzzing_input_exception();
+ }
}
try {
ds >> obj;
@@ -79,9 +85,9 @@ void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj)
}
template <typename T>
-void AssertEqualAfterSerializeDeserialize(const T& obj)
+void AssertEqualAfterSerializeDeserialize(const T& obj, const int version = INIT_PROTO_VERSION)
{
- assert(Deserialize<T>(Serialize(obj)) == obj);
+ assert(Deserialize<T>(Serialize(obj, version)) == obj);
}
} // namespace
@@ -124,9 +130,15 @@ void test_one_input(const std::vector<uint8_t>& buffer)
CScript script;
DeserializeFromFuzzingInput(buffer, script);
#elif SUB_NET_DESERIALIZE
- CSubNet sub_net;
- DeserializeFromFuzzingInput(buffer, sub_net);
- AssertEqualAfterSerializeDeserialize(sub_net);
+ 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);
#elif TX_IN_DESERIALIZE
CTxIn tx_in;
DeserializeFromFuzzingInput(buffer, tx_in);
@@ -183,11 +195,24 @@ void test_one_input(const std::vector<uint8_t>& buffer)
#elif NETADDR_DESERIALIZE
CNetAddr na;
DeserializeFromFuzzingInput(buffer, na);
- AssertEqualAfterSerializeDeserialize(na);
+ if (na.IsAddrV1Compatible()) {
+ AssertEqualAfterSerializeDeserialize(na);
+ }
+ AssertEqualAfterSerializeDeserialize(na, INIT_PROTO_VERSION | ADDRV2_FORMAT);
#elif SERVICE_DESERIALIZE
CService s;
DeserializeFromFuzzingInput(buffer, s);
- AssertEqualAfterSerializeDeserialize(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);
#elif MESSAGEHEADER_DESERIALIZE
CMessageHeader mh;
DeserializeFromFuzzingInput(buffer, mh);
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 1e1807d734..753cfffdcb 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -12,15 +12,6 @@
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
-// Decide if main(...) should be provided:
-// * AFL needs main(...) regardless of platform.
-// * macOS handles __attribute__((weak)) main(...) poorly when linking
-// against libFuzzer. See https://github.com/bitcoin/bitcoin/pull/18008
-// for details.
-#if defined(__AFL_COMPILER) || !defined(MAC_OSX)
-#define PROVIDE_MAIN_FUNCTION
-#endif
-
#if defined(PROVIDE_MAIN_FUNCTION)
static bool read_stdin(std::vector<uint8_t>& data)
{
diff --git a/src/test/fuzz/merkleblock.cpp b/src/test/fuzz/merkleblock.cpp
index c44e334272..4710e75757 100644
--- a/src/test/fuzz/merkleblock.cpp
+++ b/src/test/fuzz/merkleblock.cpp
@@ -16,12 +16,36 @@
void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- std::optional<CPartialMerkleTree> partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider);
- if (!partial_merkle_tree) {
- return;
+ CPartialMerkleTree partial_merkle_tree;
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 1)) {
+ case 0: {
+ const std::optional<CPartialMerkleTree> opt_partial_merkle_tree = ConsumeDeserializable<CPartialMerkleTree>(fuzzed_data_provider);
+ if (opt_partial_merkle_tree) {
+ partial_merkle_tree = *opt_partial_merkle_tree;
+ }
+ break;
}
- (void)partial_merkle_tree->GetNumTransactions();
+ case 1: {
+ CMerkleBlock merkle_block;
+ const std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
+ CBloomFilter bloom_filter;
+ std::set<uint256> txids;
+ if (opt_block && !opt_block->vtx.empty()) {
+ if (fuzzed_data_provider.ConsumeBool()) {
+ merkle_block = CMerkleBlock{*opt_block, bloom_filter};
+ } else if (fuzzed_data_provider.ConsumeBool()) {
+ while (fuzzed_data_provider.ConsumeBool()) {
+ txids.insert(ConsumeUInt256(fuzzed_data_provider));
+ }
+ merkle_block = CMerkleBlock{*opt_block, txids};
+ }
+ }
+ partial_merkle_tree = merkle_block.txn;
+ break;
+ }
+ }
+ (void)partial_merkle_tree.GetNumTransactions();
std::vector<uint256> matches;
std::vector<unsigned int> indices;
- (void)partial_merkle_tree->ExtractMatches(matches, indices);
+ (void)partial_merkle_tree.ExtractMatches(matches, indices);
}
diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp
index a85c353243..c61d406291 100644
--- a/src/test/fuzz/net.cpp
+++ b/src/test/fuzz/net.cpp
@@ -46,7 +46,8 @@ void test_one_input(const std::vector<uint8_t>& buffer)
fuzzed_data_provider.ConsumeIntegral<uint64_t>(),
*address_bind,
fuzzed_data_provider.ConsumeRandomLengthString(32),
- fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH})};
+ fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH}),
+ fuzzed_data_provider.ConsumeBool()};
while (fuzzed_data_provider.ConsumeBool()) {
switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 11)) {
case 0: {
@@ -62,7 +63,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
break;
}
case 3: {
- const std::vector<bool> asmap = ConsumeRandomLengthIntegralVector<bool>(fuzzed_data_provider, 128);
+ const std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
if (!SanityCheckASMap(asmap)) {
break;
}
@@ -148,4 +149,5 @@ void test_one_input(const std::vector<uint8_t>& buffer)
fuzzed_data_provider.PickValueInArray<NetPermissionFlags>({NetPermissionFlags::PF_NONE, NetPermissionFlags::PF_BLOOMFILTER, NetPermissionFlags::PF_RELAY, NetPermissionFlags::PF_FORCERELAY, NetPermissionFlags::PF_NOBAN, NetPermissionFlags::PF_MEMPOOL, NetPermissionFlags::PF_ISIMPLICIT, NetPermissionFlags::PF_ALL}) :
static_cast<NetPermissionFlags>(fuzzed_data_provider.ConsumeIntegral<uint32_t>());
(void)node.HasPermission(net_permission_flags);
+ (void)node.ConnectedThroughNetwork();
}
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 3ef03137ec..9390399878 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -16,6 +16,7 @@
#include <test/util/mining.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
+#include <test/util/validation.h>
#include <util/memory.h>
#include <validationinterface.h>
#include <version.h>
@@ -63,10 +64,14 @@ void test_one_input(const std::vector<uint8_t>& buffer)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
ConnmanTestMsg& connman = *(ConnmanTestMsg*)g_setup->m_node.connman.get();
+ TestChainState& chainstate = *(TestChainState*)&g_setup->m_node.chainman->ActiveChainstate();
+ chainstate.ResetIbd();
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()};
if (!LIMIT_TO_MESSAGE_TYPE.empty() && random_message_type != LIMIT_TO_MESSAGE_TYPE) {
return;
}
+ const bool jump_out_of_ibd{fuzzed_data_provider.ConsumeBool()};
+ if (jump_out_of_ibd) chainstate.JumpOutOfIbd();
CDataStream random_bytes_data_stream{fuzzed_data_provider.ConsumeRemainingBytes<unsigned char>(), SER_NETWORK, PROTOCOL_VERSION};
CNode& p2p_node = *MakeUnique<CNode>(0, ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_BLOOM), 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, ConnectionType::OUTBOUND_FULL_RELAY).release();
p2p_node.fSuccessfullyConnected = true;
@@ -76,7 +81,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
g_setup->m_node.peerman->InitializeNode(&p2p_node);
try {
g_setup->m_node.peerman->ProcessMessage(p2p_node, random_message_type, random_bytes_data_stream,
- GetTime<std::chrono::microseconds>(), std::atomic<bool>{false});
+ GetTime<std::chrono::microseconds>(), std::atomic<bool>{false});
} catch (const std::ios_base::failure&) {
}
SyncWithValidationInterfaceQueue();
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index f722eeac3a..19ea92b750 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -12,6 +12,7 @@
#include <test/util/mining.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
+#include <test/util/validation.h>
#include <util/memory.h>
#include <validation.h>
#include <validationinterface.h>
@@ -39,7 +40,10 @@ void test_one_input(const std::vector<uint8_t>& buffer)
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
ConnmanTestMsg& connman = *(ConnmanTestMsg*)g_setup->m_node.connman.get();
+ TestChainState& chainstate = *(TestChainState*)&g_setup->m_node.chainman->ActiveChainstate();
+ chainstate.ResetIbd();
std::vector<CNode*> peers;
+ bool jump_out_of_ibd{false};
const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
for (int i = 0; i < num_peers_to_add; ++i) {
@@ -58,6 +62,8 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
while (fuzzed_data_provider.ConsumeBool()) {
+ if (!jump_out_of_ibd) jump_out_of_ibd = fuzzed_data_provider.ConsumeBool();
+ if (jump_out_of_ibd && chainstate.IsInitialBlockDownload()) chainstate.JumpOutOfIbd();
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()};
CSerializedNetMsg net_msg;
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp
new file mode 100644
index 0000000000..d20fa43d68
--- /dev/null
+++ b/src/test/fuzz/script_assets_test_minimizer.cpp
@@ -0,0 +1,200 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/fuzz/fuzz.h>
+
+#include <primitives/transaction.h>
+#include <pubkey.h>
+#include <script/interpreter.h>
+#include <serialize.h>
+#include <streams.h>
+#include <univalue.h>
+#include <util/strencodings.h>
+
+#include <boost/algorithm/string.hpp>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+// This fuzz "test" can be used to minimize test cases for script_assets_test in
+// src/test/script_tests.cpp. While it written as a fuzz test, and can be used as such,
+// fuzzing the inputs is unlikely to construct useful test cases.
+//
+// Instead, it is primarily intended to be run on a test set that was generated
+// externally, for example using test/functional/feature_taproot.py's --dumptests mode.
+// The minimized set can then be concatenated together, surrounded by '[' and ']',
+// and used as the script_assets_test.json input to the script_assets_test unit test:
+//
+// (normal build)
+// $ mkdir dump
+// $ for N in $(seq 1 10); do TEST_DUMP_DIR=dump test/functional/feature_taproot --dumptests; done
+// $ ...
+//
+// (fuzz test build)
+// $ mkdir dump-min
+// $ ./src/test/fuzz/script_assets_test_minimizer -merge=1 dump-min/ dump/
+// $ (echo -en '[\n'; cat dump-min/* | head -c -2; echo -en '\n]') >script_assets_test.json
+
+namespace {
+
+std::vector<unsigned char> CheckedParseHex(const std::string& str)
+{
+ if (str.size() && !IsHex(str)) throw std::runtime_error("Non-hex input '" + str + "'");
+ return ParseHex(str);
+}
+
+CScript ScriptFromHex(const std::string& str)
+{
+ std::vector<unsigned char> data = CheckedParseHex(str);
+ return CScript(data.begin(), data.end());
+}
+
+CMutableTransaction TxFromHex(const std::string& str)
+{
+ CMutableTransaction tx;
+ try {
+ VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, CheckedParseHex(str), 0) >> tx;
+ } catch (const std::ios_base::failure&) {
+ throw std::runtime_error("Tx deserialization failure");
+ }
+ return tx;
+}
+
+std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
+{
+ if (!univalue.isArray()) throw std::runtime_error("Prevouts must be array");
+ std::vector<CTxOut> prevouts;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ CTxOut txout;
+ try {
+ VectorReader(SER_DISK, 0, CheckedParseHex(univalue[i].get_str()), 0) >> txout;
+ } catch (const std::ios_base::failure&) {
+ throw std::runtime_error("Prevout invalid format");
+ }
+ prevouts.push_back(std::move(txout));
+ }
+ return prevouts;
+}
+
+CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
+{
+ if (!univalue.isArray()) throw std::runtime_error("Script witness is not array");
+ CScriptWitness scriptwitness;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ auto bytes = CheckedParseHex(univalue[i].get_str());
+ scriptwitness.stack.push_back(std::move(bytes));
+ }
+ return scriptwitness;
+}
+
+const std::map<std::string, unsigned int> FLAG_NAMES = {
+ {std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
+ {std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
+ {std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
+ {std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
+ {std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
+ {std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
+ {std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
+};
+
+std::vector<unsigned int> AllFlags()
+{
+ std::vector<unsigned int> ret;
+
+ for (unsigned int i = 0; i < 128; ++i) {
+ unsigned int flag = 0;
+ if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
+ if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
+ if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
+ if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
+ if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
+ if (i & 32) flag |= SCRIPT_VERIFY_WITNESS;
+ if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT;
+
+ // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH
+ if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue;
+ // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS
+ if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue;
+
+ ret.push_back(flag);
+ }
+
+ return ret;
+}
+
+const std::vector<unsigned int> ALL_FLAGS = AllFlags();
+
+unsigned int ParseScriptFlags(const std::string& str)
+{
+ if (str.empty()) return 0;
+
+ unsigned int flags = 0;
+ std::vector<std::string> words;
+ boost::algorithm::split(words, str, boost::algorithm::is_any_of(","));
+
+ for (const std::string& word : words)
+ {
+ auto it = FLAG_NAMES.find(word);
+ if (it == FLAG_NAMES.end()) throw std::runtime_error("Unknown verification flag " + word);
+ flags |= it->second;
+ }
+
+ return flags;
+}
+
+void Test(const std::string& str)
+{
+ UniValue test;
+ if (!test.read(str) || !test.isObject()) throw std::runtime_error("Non-object test input");
+
+ CMutableTransaction tx = TxFromHex(test["tx"].get_str());
+ const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
+ if (prevouts.size() != tx.vin.size()) throw std::runtime_error("Incorrect number of prevouts");
+ size_t idx = test["index"].get_int64();
+ if (idx >= tx.vin.size()) throw std::runtime_error("Invalid index");
+ unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
+ bool final = test.exists("final") && test["final"].get_bool();
+
+ if (test.exists("success")) {
+ tx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str());
+ tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
+ PrecomputedTransactionData txdata;
+ txdata.Init(tx, std::vector<CTxOut>(prevouts));
+ MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
+ for (const auto flags : ALL_FLAGS) {
+ // "final": true tests are valid for all flags. Others are only valid with flags that are
+ // a subset of test_flags.
+ if (final || ((flags & test_flags) == flags)) {
+ (void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
+ }
+ }
+ }
+
+ if (test.exists("failure")) {
+ tx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
+ tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
+ PrecomputedTransactionData txdata;
+ txdata.Init(tx, std::vector<CTxOut>(prevouts));
+ MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
+ for (const auto flags : ALL_FLAGS) {
+ // If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
+ if ((flags & test_flags) == test_flags) {
+ (void)VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
+ }
+ }
+ }
+}
+
+ECCVerifyHandle handle;
+
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return;
+ const std::string str((const char*)buffer.data(), buffer.size() - 2);
+ try {
+ Test(str);
+ } catch (const std::runtime_error&) {}
+}
diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp
index ffc65eedc0..300c78fca0 100644
--- a/src/test/fuzz/script_flags.cpp
+++ b/src/test/fuzz/script_flags.cpp
@@ -31,7 +31,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
try {
const CTransaction tx(deserialize, ds);
- const PrecomputedTransactionData txdata(tx);
unsigned int verify_flags;
ds >> verify_flags;
@@ -41,10 +40,17 @@ void test_one_input(const std::vector<uint8_t>& buffer)
unsigned int fuzzed_flags;
ds >> fuzzed_flags;
+ std::vector<CTxOut> spent_outputs;
for (unsigned i = 0; i < tx.vin.size(); ++i) {
CTxOut prevout;
ds >> prevout;
+ spent_outputs.push_back(prevout);
+ }
+ PrecomputedTransactionData txdata;
+ txdata.Init(tx, std::move(spent_outputs));
+ for (unsigned i = 0; i < tx.vin.size(); ++i) {
+ const CTxOut& prevout = txdata.m_spent_outputs.at(i);
const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata};
ScriptError serror;
diff --git a/src/test/fuzz/script_sigcache.cpp b/src/test/fuzz/script_sigcache.cpp
index 434a47b702..87af71897b 100644
--- a/src/test/fuzz/script_sigcache.cpp
+++ b/src/test/fuzz/script_sigcache.cpp
@@ -35,11 +35,19 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const bool store = fuzzed_data_provider.ConsumeBool();
PrecomputedTransactionData tx_data;
CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
- const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
- if (pub_key) {
- const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
- if (!random_bytes.empty()) {
- (void)caching_transaction_signature_checker.VerifySignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
+ if (fuzzed_data_provider.ConsumeBool()) {
+ const auto random_bytes = fuzzed_data_provider.ConsumeBytes<unsigned char>(64);
+ const XOnlyPubKey pub_key(ConsumeUInt256(fuzzed_data_provider));
+ if (random_bytes.size() == 64) {
+ (void)caching_transaction_signature_checker.VerifySchnorrSignature(random_bytes, pub_key, ConsumeUInt256(fuzzed_data_provider));
+ }
+ } else {
+ const auto random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ const auto pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
+ if (pub_key) {
+ if (!random_bytes.empty()) {
+ (void)caching_transaction_signature_checker.VerifyECDSASignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
+ }
}
}
}
diff --git a/src/test/fuzz/signature_checker.cpp b/src/test/fuzz/signature_checker.cpp
index 3aaeb66649..e121c89665 100644
--- a/src/test/fuzz/signature_checker.cpp
+++ b/src/test/fuzz/signature_checker.cpp
@@ -28,7 +28,12 @@ public:
{
}
- bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
+ bool CheckECDSASignature(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override
+ {
+ return m_fuzzed_data_provider.ConsumeBool();
+ }
+
+ bool CheckSchnorrSignature(Span<const unsigned char> sig, Span<const unsigned char> pubkey, SigVersion sigversion, const ScriptExecutionData& execdata, ScriptError* serror = nullptr) const override
{
return m_fuzzed_data_provider.ConsumeBool();
}
diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
index d6deb7fc3d..4f972dea1c 100644
--- a/src/test/fuzz/transaction.cpp
+++ b/src/test/fuzz/transaction.cpp
@@ -95,7 +95,8 @@ void test_one_input(const std::vector<uint8_t>& buffer)
CCoinsView coins_view;
const CCoinsViewCache coins_view_cache(&coins_view);
- (void)AreInputsStandard(tx, coins_view_cache);
+ (void)AreInputsStandard(tx, coins_view_cache, false);
+ (void)AreInputsStandard(tx, coins_view_cache, true);
(void)IsWitnessStandard(tx, coins_view_cache);
UniValue u(UniValue::VOBJ);
diff --git a/src/test/fuzz/txrequest.cpp b/src/test/fuzz/txrequest.cpp
new file mode 100644
index 0000000000..9529ad3274
--- /dev/null
+++ b/src/test/fuzz/txrequest.cpp
@@ -0,0 +1,374 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/common.h>
+#include <crypto/sha256.h>
+#include <crypto/siphash.h>
+#include <primitives/transaction.h>
+#include <test/fuzz/fuzz.h>
+#include <txrequest.h>
+
+#include <bitset>
+#include <cstdint>
+#include <queue>
+#include <vector>
+
+namespace {
+
+constexpr int MAX_TXHASHES = 16;
+constexpr int MAX_PEERS = 16;
+
+//! Randomly generated GenTxids used in this test (length is MAX_TXHASHES).
+uint256 TXHASHES[MAX_TXHASHES];
+
+//! Precomputed random durations (positive and negative, each ~exponentially distributed).
+std::chrono::microseconds DELAYS[256];
+
+struct Initializer
+{
+ Initializer()
+ {
+ for (uint8_t txhash = 0; txhash < MAX_TXHASHES; txhash += 1) {
+ CSHA256().Write(&txhash, 1).Finalize(TXHASHES[txhash].begin());
+ }
+ int i = 0;
+ // DELAYS[N] for N=0..15 is just N microseconds.
+ for (; i < 16; ++i) {
+ DELAYS[i] = std::chrono::microseconds{i};
+ }
+ // DELAYS[N] for N=16..127 has randomly-looking but roughly exponentially increasing values up to
+ // 198.416453 seconds.
+ for (; i < 128; ++i) {
+ int diff_bits = ((i - 10) * 2) / 9;
+ uint64_t diff = 1 + (CSipHasher(0, 0).Write(i).Finalize() >> (64 - diff_bits));
+ DELAYS[i] = DELAYS[i - 1] + std::chrono::microseconds{diff};
+ }
+ // DELAYS[N] for N=128..255 are negative delays with the same magnitude as N=0..127.
+ for (; i < 256; ++i) {
+ DELAYS[i] = -DELAYS[255 - i];
+ }
+ }
+} g_initializer;
+
+/** Tester class for TxRequestTracker
+ *
+ * It includes a naive reimplementation of its behavior, for a limited set
+ * of MAX_TXHASHES distinct txids, and MAX_PEERS peer identifiers.
+ *
+ * All of the public member functions perform the same operation on
+ * an actual TxRequestTracker and on the state of the reimplementation.
+ * The output of GetRequestable is compared with the expected value
+ * as well.
+ *
+ * Check() calls the TxRequestTracker's sanity check, plus compares the
+ * output of the constant accessors (Size(), CountLoad(), CountTracked())
+ * with expected values.
+ */
+class Tester
+{
+ //! TxRequestTracker object being tested.
+ TxRequestTracker m_tracker;
+
+ //! States for txid/peer combinations in the naive data structure.
+ enum class State {
+ NOTHING, //!< Absence of this txid/peer combination
+
+ // Note that this implementation does not distinguish between DELAYED/READY/BEST variants of CANDIDATE.
+ CANDIDATE,
+ REQUESTED,
+ COMPLETED,
+ };
+
+ //! Sequence numbers, incremented whenever a new CANDIDATE is added.
+ uint64_t m_current_sequence{0};
+
+ //! List of future 'events' (all inserted reqtimes/exptimes). This is used to implement AdvanceToEvent.
+ std::priority_queue<std::chrono::microseconds, std::vector<std::chrono::microseconds>,
+ std::greater<std::chrono::microseconds>> m_events;
+
+ //! Information about a txhash/peer combination.
+ struct Announcement
+ {
+ std::chrono::microseconds m_time;
+ uint64_t m_sequence;
+ State m_state{State::NOTHING};
+ bool m_preferred;
+ bool m_is_wtxid;
+ uint64_t m_priority; //!< Precomputed priority.
+ };
+
+ //! Information about all txhash/peer combination.
+ Announcement m_announcements[MAX_TXHASHES][MAX_PEERS];
+
+ //! The current time; can move forward and backward.
+ std::chrono::microseconds m_now{244466666};
+
+ //! Delete txhashes whose only announcements are COMPLETED.
+ void Cleanup(int txhash)
+ {
+ bool all_nothing = true;
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ const Announcement& ann = m_announcements[txhash][peer];
+ if (ann.m_state != State::NOTHING) {
+ if (ann.m_state != State::COMPLETED) return;
+ all_nothing = false;
+ }
+ }
+ if (all_nothing) return;
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ m_announcements[txhash][peer].m_state = State::NOTHING;
+ }
+ }
+
+ //! Find the current best peer to request from for a txhash (or -1 if none).
+ int GetSelected(int txhash) const
+ {
+ int ret = -1;
+ uint64_t ret_priority = 0;
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ const Announcement& ann = m_announcements[txhash][peer];
+ // Return -1 if there already is a (non-expired) in-flight request.
+ if (ann.m_state == State::REQUESTED) return -1;
+ // If it's a viable candidate, see if it has lower priority than the best one so far.
+ if (ann.m_state == State::CANDIDATE && ann.m_time <= m_now) {
+ if (ret == -1 || ann.m_priority > ret_priority) {
+ std::tie(ret, ret_priority) = std::tie(peer, ann.m_priority);
+ }
+ }
+ }
+ return ret;
+ }
+
+public:
+ Tester() : m_tracker(true) {}
+
+ std::chrono::microseconds Now() const { return m_now; }
+
+ void AdvanceTime(std::chrono::microseconds offset)
+ {
+ m_now += offset;
+ while (!m_events.empty() && m_events.top() <= m_now) m_events.pop();
+ }
+
+ void AdvanceToEvent()
+ {
+ while (!m_events.empty() && m_events.top() <= m_now) m_events.pop();
+ if (!m_events.empty()) {
+ m_now = m_events.top();
+ m_events.pop();
+ }
+ }
+
+ void DisconnectedPeer(int peer)
+ {
+ // Apply to naive structure: all announcements for that peer are wiped.
+ for (int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {
+ if (m_announcements[txhash][peer].m_state != State::NOTHING) {
+ m_announcements[txhash][peer].m_state = State::NOTHING;
+ Cleanup(txhash);
+ }
+ }
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.DisconnectedPeer(peer);
+ }
+
+ void ForgetTxHash(int txhash)
+ {
+ // Apply to naive structure: all announcements for that txhash are wiped.
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ m_announcements[txhash][peer].m_state = State::NOTHING;
+ }
+ Cleanup(txhash);
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.ForgetTxHash(TXHASHES[txhash]);
+ }
+
+ void ReceivedInv(int peer, int txhash, bool is_wtxid, bool preferred, std::chrono::microseconds reqtime)
+ {
+ // Apply to naive structure: if no announcement for txidnum/peer combination
+ // already, create a new CANDIDATE; otherwise do nothing.
+ Announcement& ann = m_announcements[txhash][peer];
+ if (ann.m_state == State::NOTHING) {
+ ann.m_preferred = preferred;
+ ann.m_state = State::CANDIDATE;
+ ann.m_time = reqtime;
+ ann.m_is_wtxid = is_wtxid;
+ ann.m_sequence = m_current_sequence++;
+ ann.m_priority = m_tracker.ComputePriority(TXHASHES[txhash], peer, ann.m_preferred);
+
+ // Add event so that AdvanceToEvent can quickly jump to the point where its reqtime passes.
+ if (reqtime > m_now) m_events.push(reqtime);
+ }
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.ReceivedInv(peer, GenTxid{is_wtxid, TXHASHES[txhash]}, preferred, reqtime);
+ }
+
+ void RequestedTx(int peer, int txhash, std::chrono::microseconds exptime)
+ {
+ // Apply to naive structure: if a CANDIDATE announcement exists for peer/txhash,
+ // convert it to REQUESTED, and change any existing REQUESTED announcement for the same txhash to COMPLETED.
+ if (m_announcements[txhash][peer].m_state == State::CANDIDATE) {
+ for (int peer2 = 0; peer2 < MAX_PEERS; ++peer2) {
+ if (m_announcements[txhash][peer2].m_state == State::REQUESTED) {
+ m_announcements[txhash][peer2].m_state = State::COMPLETED;
+ }
+ }
+ m_announcements[txhash][peer].m_state = State::REQUESTED;
+ m_announcements[txhash][peer].m_time = exptime;
+ }
+
+ // Add event so that AdvanceToEvent can quickly jump to the point where its exptime passes.
+ if (exptime > m_now) m_events.push(exptime);
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.RequestedTx(peer, TXHASHES[txhash], exptime);
+ }
+
+ void ReceivedResponse(int peer, int txhash)
+ {
+ // Apply to naive structure: convert anything to COMPLETED.
+ if (m_announcements[txhash][peer].m_state != State::NOTHING) {
+ m_announcements[txhash][peer].m_state = State::COMPLETED;
+ Cleanup(txhash);
+ }
+
+ // Call TxRequestTracker's implementation.
+ m_tracker.ReceivedResponse(peer, TXHASHES[txhash]);
+ }
+
+ void GetRequestable(int peer)
+ {
+ // Implement using naive structure:
+
+ //! list of (sequence number, txhash, is_wtxid) tuples.
+ std::vector<std::tuple<uint64_t, int, bool>> result;
+ std::vector<std::pair<NodeId, GenTxid>> expected_expired;
+ for (int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {
+ // Mark any expired REQUESTED announcements as COMPLETED.
+ for (int peer2 = 0; peer2 < MAX_PEERS; ++peer2) {
+ Announcement& ann2 = m_announcements[txhash][peer2];
+ if (ann2.m_state == State::REQUESTED && ann2.m_time <= m_now) {
+ expected_expired.emplace_back(peer2, GenTxid{ann2.m_is_wtxid, TXHASHES[txhash]});
+ ann2.m_state = State::COMPLETED;
+ break;
+ }
+ }
+ // And delete txids with only COMPLETED announcements left.
+ Cleanup(txhash);
+ // CANDIDATEs for which this announcement has the highest priority get returned.
+ const Announcement& ann = m_announcements[txhash][peer];
+ if (ann.m_state == State::CANDIDATE && GetSelected(txhash) == peer) {
+ result.emplace_back(ann.m_sequence, txhash, ann.m_is_wtxid);
+ }
+ }
+ // Sort the results by sequence number.
+ std::sort(result.begin(), result.end());
+ std::sort(expected_expired.begin(), expected_expired.end());
+
+ // Compare with TxRequestTracker's implementation.
+ std::vector<std::pair<NodeId, GenTxid>> expired;
+ const auto actual = m_tracker.GetRequestable(peer, m_now, &expired);
+ std::sort(expired.begin(), expired.end());
+ assert(expired == expected_expired);
+
+ m_tracker.PostGetRequestableSanityCheck(m_now);
+ assert(result.size() == actual.size());
+ for (size_t pos = 0; pos < actual.size(); ++pos) {
+ assert(TXHASHES[std::get<1>(result[pos])] == actual[pos].GetHash());
+ assert(std::get<2>(result[pos]) == actual[pos].IsWtxid());
+ }
+ }
+
+ void Check()
+ {
+ // Compare CountTracked and CountLoad with naive structure.
+ size_t total = 0;
+ for (int peer = 0; peer < MAX_PEERS; ++peer) {
+ size_t tracked = 0;
+ size_t inflight = 0;
+ size_t candidates = 0;
+ for (int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {
+ tracked += m_announcements[txhash][peer].m_state != State::NOTHING;
+ inflight += m_announcements[txhash][peer].m_state == State::REQUESTED;
+ candidates += m_announcements[txhash][peer].m_state == State::CANDIDATE;
+ }
+ assert(m_tracker.Count(peer) == tracked);
+ assert(m_tracker.CountInFlight(peer) == inflight);
+ assert(m_tracker.CountCandidates(peer) == candidates);
+ total += tracked;
+ }
+ // Compare Size.
+ assert(m_tracker.Size() == total);
+
+ // Invoke internal consistency check of TxRequestTracker object.
+ m_tracker.SanityCheck();
+ }
+};
+} // namespace
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ // Tester object (which encapsulates a TxRequestTracker).
+ Tester tester;
+
+ // Decode the input as a sequence of instructions with parameters
+ auto it = buffer.begin();
+ while (it != buffer.end()) {
+ int cmd = *(it++) % 11;
+ int peer, txidnum, delaynum;
+ switch (cmd) {
+ case 0: // Make time jump to the next event (m_time of CANDIDATE or REQUESTED)
+ tester.AdvanceToEvent();
+ break;
+ case 1: // Change time
+ delaynum = it == buffer.end() ? 0 : *(it++);
+ tester.AdvanceTime(DELAYS[delaynum]);
+ break;
+ case 2: // Query for requestable txs
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ tester.GetRequestable(peer);
+ break;
+ case 3: // Peer went offline
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ tester.DisconnectedPeer(peer);
+ break;
+ case 4: // No longer need tx
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ tester.ForgetTxHash(txidnum % MAX_TXHASHES);
+ break;
+ case 5: // Received immediate preferred inv
+ case 6: // Same, but non-preferred.
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ tester.ReceivedInv(peer, txidnum % MAX_TXHASHES, (txidnum / MAX_TXHASHES) & 1, cmd & 1,
+ std::chrono::microseconds::min());
+ break;
+ case 7: // Received delayed preferred inv
+ case 8: // Same, but non-preferred.
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ delaynum = it == buffer.end() ? 0 : *(it++);
+ tester.ReceivedInv(peer, txidnum % MAX_TXHASHES, (txidnum / MAX_TXHASHES) & 1, cmd & 1,
+ tester.Now() + DELAYS[delaynum]);
+ break;
+ case 9: // Requested tx from peer
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ delaynum = it == buffer.end() ? 0 : *(it++);
+ tester.RequestedTx(peer, txidnum % MAX_TXHASHES, tester.Now() + DELAYS[delaynum]);
+ break;
+ case 10: // Received response
+ peer = it == buffer.end() ? 0 : *(it++) % MAX_PEERS;
+ txidnum = it == buffer.end() ? 0 : *(it++);
+ tester.ReceivedResponse(peer, txidnum % MAX_TXHASHES);
+ break;
+ default:
+ assert(false);
+ }
+ }
+ tester.Check();
+}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index ed6093a8a8..e99ed8d72d 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -11,6 +11,8 @@
#include <chainparamsbase.h>
#include <coins.h>
#include <consensus/consensus.h>
+#include <merkleblock.h>
+#include <net.h>
#include <netaddress.h>
#include <netbase.h>
#include <primitives/transaction.h>
@@ -23,6 +25,7 @@
#include <test/util/setup_common.h>
#include <txmempool.h>
#include <uint256.h>
+#include <util/time.h>
#include <version.h>
#include <algorithm>
@@ -38,6 +41,11 @@ NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataPr
return {s.begin(), s.end()};
}
+NODISCARD inline std::vector<bool> ConsumeRandomLengthBitVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+{
+ return BytesToBits(ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length));
+}
+
NODISCARD inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
{
return {ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION};
@@ -88,6 +96,13 @@ NODISCARD inline CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider)
return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, MAX_MONEY);
}
+NODISCARD inline int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:00Z");
+ static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z");
+ return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(time_min, time_max);
+}
+
NODISCARD inline CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept
{
const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
@@ -260,6 +275,32 @@ CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint8_t>()};
}
+CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()};
+}
+
+CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ return {ConsumeService(fuzzed_data_provider), static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>()), fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
+}
+
+CNode ConsumeNode(FuzzedDataProvider& fuzzed_data_provider) noexcept
+{
+ const NodeId node_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
+ const ServiceFlags local_services = static_cast<ServiceFlags>(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ const int my_starting_height = fuzzed_data_provider.ConsumeIntegral<int>();
+ const SOCKET socket = INVALID_SOCKET;
+ const CAddress address = ConsumeAddress(fuzzed_data_provider);
+ const uint64_t keyed_net_group = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
+ const uint64_t local_host_nonce = fuzzed_data_provider.ConsumeIntegral<uint64_t>();
+ const CAddress addr_bind = ConsumeAddress(fuzzed_data_provider);
+ const std::string addr_name = fuzzed_data_provider.ConsumeRandomLengthString(64);
+ const ConnectionType conn_type = fuzzed_data_provider.PickValueInArray({ConnectionType::INBOUND, ConnectionType::OUTBOUND_FULL_RELAY, ConnectionType::MANUAL, ConnectionType::FEELER, ConnectionType::BLOCK_RELAY, ConnectionType::ADDR_FETCH});
+ const bool inbound_onion = fuzzed_data_provider.ConsumeBool();
+ return {node_id, local_services, my_starting_height, socket, address, keyed_net_group, local_host_nonce, addr_bind, addr_name, conn_type, inbound_onion};
+}
+
void InitializeFuzzingContext(const std::string& chain_name = CBaseChainParams::REGTEST)
{
static const BasicTestingSetup basic_testing_setup{chain_name, {"-nodebuglogfile"}};
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index 4e4c44266a..3362b8d17c 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -264,4 +264,32 @@ BOOST_AUTO_TEST_CASE(pubkey_unserialize)
}
}
+BOOST_AUTO_TEST_CASE(bip340_test_vectors)
+{
+ static const std::vector<std::pair<std::array<std::string, 3>, bool>> VECTORS = {
+ {{"F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", "0000000000000000000000000000000000000000000000000000000000000000", "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"}, true},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"}, true},
+ {{"DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"}, true},
+ {{"25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"}, true},
+ {{"D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703", "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4"}, true},
+ {{"EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false},
+ {{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"}, false},
+ {{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"}, false}
+ };
+
+ for (const auto& test : VECTORS) {
+ auto pubkey = ParseHex(test.first[0]);
+ auto msg = ParseHex(test.first[1]);
+ auto sig = ParseHex(test.first[2]);
+ BOOST_CHECK_EQUAL(XOnlyPubKey(pubkey).VerifySchnorr(uint256(msg), sig), test.second);
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/limitedmap_tests.cpp b/src/test/limitedmap_tests.cpp
deleted file mode 100644
index ea18debbd3..0000000000
--- a/src/test/limitedmap_tests.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright (c) 2012-2019 The Bitcoin Core developers
-// Distributed under the MIT software license, see the accompanying
-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-#include <limitedmap.h>
-
-#include <test/util/setup_common.h>
-
-#include <boost/test/unit_test.hpp>
-
-BOOST_FIXTURE_TEST_SUITE(limitedmap_tests, BasicTestingSetup)
-
-BOOST_AUTO_TEST_CASE(limitedmap_test)
-{
- // create a limitedmap capped at 10 items
- limitedmap<int, int> map(10);
-
- // check that the max size is 10
- BOOST_CHECK(map.max_size() == 10);
-
- // check that it's empty
- BOOST_CHECK(map.size() == 0);
-
- // insert (-1, -1)
- map.insert(std::pair<int, int>(-1, -1));
-
- // make sure that the size is updated
- BOOST_CHECK(map.size() == 1);
-
- // make sure that the new item is in the map
- BOOST_CHECK(map.count(-1) == 1);
-
- // insert 10 new items
- for (int i = 0; i < 10; i++) {
- map.insert(std::pair<int, int>(i, i + 1));
- }
-
- // make sure that the map now contains 10 items...
- BOOST_CHECK(map.size() == 10);
-
- // ...and that the first item has been discarded
- BOOST_CHECK(map.count(-1) == 0);
-
- // iterate over the map, both with an index and an iterator
- limitedmap<int, int>::const_iterator it = map.begin();
- for (int i = 0; i < 10; i++) {
- // make sure the item is present
- BOOST_CHECK(map.count(i) == 1);
-
- // use the iterator to check for the expected key and value
- BOOST_CHECK(it->first == i);
- BOOST_CHECK(it->second == i + 1);
-
- // use find to check for the value
- BOOST_CHECK(map.find(i)->second == i + 1);
-
- // update and recheck
- map.update(it, i + 2);
- BOOST_CHECK(map.find(i)->second == i + 2);
-
- it++;
- }
-
- // check that we've exhausted the iterator
- BOOST_CHECK(it == map.end());
-
- // resize the map to 5 items
- map.max_size(5);
-
- // check that the max size and size are now 5
- BOOST_CHECK(map.max_size() == 5);
- BOOST_CHECK(map.size() == 5);
-
- // check that items less than 5 have been discarded
- // and items greater than 5 are retained
- for (int i = 0; i < 10; i++) {
- if (i < 5) {
- BOOST_CHECK(map.count(i) == 0);
- } else {
- BOOST_CHECK(map.count(i) == 1);
- }
- }
-
- // erase some items not in the map
- for (int i = 100; i < 1000; i += 100) {
- map.erase(i);
- }
-
- // check that the size is unaffected
- BOOST_CHECK(map.size() == 5);
-
- // erase the remaining elements
- for (int i = 5; i < 10; i++) {
- map.erase(i);
- }
-
- // check that the map is now empty
- BOOST_CHECK(map.empty());
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 8686012af7..3de79a9f45 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -198,7 +198,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
{
// Note that by default, these tests run with size accounting enabled.
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const CChainParams& chainparams = *chainParams;
CScript scriptPubKey = CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f") << OP_CHECKSIG;
std::unique_ptr<CBlockTemplate> pblocktemplate;
diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp
index 261396cd0c..37eca8b7ef 100644
--- a/src/test/net_tests.cpp
+++ b/src/test/net_tests.cpp
@@ -185,21 +185,60 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
std::string pszDest;
- std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY);
+ std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(
+ id++, NODE_NETWORK, height, hSocket, addr,
+ /* nKeyedNetGroupIn = */ 0,
+ /* nLocalHostNonceIn = */ 0,
+ CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY);
BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
BOOST_CHECK(pnode1->IsManualConn() == false);
BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
BOOST_CHECK(pnode1->IsFeelerConn() == false);
BOOST_CHECK(pnode1->IsAddrFetchConn() == false);
BOOST_CHECK(pnode1->IsInboundConn() == false);
-
- std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, ConnectionType::INBOUND);
+ BOOST_CHECK_EQUAL(pnode1->ConnectedThroughNetwork(), Network::NET_IPV4);
+
+ std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(
+ id++, NODE_NETWORK, height, hSocket, addr,
+ /* nKeyedNetGroupIn = */ 1,
+ /* nLocalHostNonceIn = */ 1,
+ CAddress(), pszDest, ConnectionType::INBOUND,
+ /* inbound_onion = */ false);
BOOST_CHECK(pnode2->IsFullOutboundConn() == false);
BOOST_CHECK(pnode2->IsManualConn() == false);
BOOST_CHECK(pnode2->IsBlockOnlyConn() == false);
BOOST_CHECK(pnode2->IsFeelerConn() == false);
BOOST_CHECK(pnode2->IsAddrFetchConn() == false);
BOOST_CHECK(pnode2->IsInboundConn() == true);
+ BOOST_CHECK_EQUAL(pnode2->ConnectedThroughNetwork(), Network::NET_IPV4);
+
+ std::unique_ptr<CNode> pnode3 = MakeUnique<CNode>(
+ id++, NODE_NETWORK, height, hSocket, addr,
+ /* nKeyedNetGroupIn = */ 0,
+ /* nLocalHostNonceIn = */ 0,
+ CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY,
+ /* inbound_onion = */ true);
+ BOOST_CHECK(pnode3->IsFullOutboundConn() == true);
+ BOOST_CHECK(pnode3->IsManualConn() == false);
+ BOOST_CHECK(pnode3->IsBlockOnlyConn() == false);
+ BOOST_CHECK(pnode3->IsFeelerConn() == false);
+ BOOST_CHECK(pnode3->IsAddrFetchConn() == false);
+ BOOST_CHECK(pnode3->IsInboundConn() == false);
+ BOOST_CHECK_EQUAL(pnode3->ConnectedThroughNetwork(), Network::NET_IPV4);
+
+ std::unique_ptr<CNode> pnode4 = MakeUnique<CNode>(
+ id++, NODE_NETWORK, height, hSocket, addr,
+ /* nKeyedNetGroupIn = */ 1,
+ /* nLocalHostNonceIn = */ 1,
+ CAddress(), pszDest, ConnectionType::INBOUND,
+ /* inbound_onion = */ true);
+ BOOST_CHECK(pnode4->IsFullOutboundConn() == false);
+ BOOST_CHECK(pnode4->IsManualConn() == false);
+ BOOST_CHECK(pnode4->IsBlockOnlyConn() == false);
+ BOOST_CHECK(pnode4->IsFeelerConn() == false);
+ BOOST_CHECK(pnode4->IsAddrFetchConn() == false);
+ BOOST_CHECK(pnode4->IsInboundConn() == true);
+ BOOST_CHECK_EQUAL(pnode4->ConnectedThroughNetwork(), Network::NET_ONION);
}
BOOST_AUTO_TEST_CASE(cnetaddr_basic)
@@ -212,6 +251,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsIPv4());
BOOST_CHECK(addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "0.0.0.0");
// IPv4, INADDR_NONE
@@ -220,6 +260,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsIPv4());
BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "255.255.255.255");
// IPv4, casual
@@ -228,6 +269,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsIPv4());
BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "12.34.56.78");
// IPv6, in6addr_any
@@ -236,6 +278,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsIPv6());
BOOST_CHECK(addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "::");
// IPv6, casual
@@ -244,14 +287,35 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsIPv6());
BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "1122:3344:5566:7788:9900:aabb:ccdd:eeff");
+ // IPv6, scoped/link-local. See https://tools.ietf.org/html/rfc4007
+ // We support non-negative decimal integers (uint32_t) as zone id indices.
+ // Test with a fairly-high value, e.g. 32, to avoid locally reserved ids.
+ const std::string link_local{"fe80::1"};
+ const std::string scoped_addr{link_local + "%32"};
+ BOOST_REQUIRE(LookupHost(scoped_addr, addr, false));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv6());
+ BOOST_CHECK(!addr.IsBindAny());
+ const std::string addr_str{addr.ToString()};
+ BOOST_CHECK(addr_str == scoped_addr || addr_str == "fe80:0:0:0:0:0:0:1");
+ // The fallback case "fe80:0:0:0:0:0:0:1" is needed for macOS 10.14/10.15 and (probably) later.
+ // Test that the delimiter "%" and default zone id of 0 can be omitted for the default scope.
+ BOOST_REQUIRE(LookupHost(link_local + "%0", addr, false));
+ BOOST_REQUIRE(addr.IsValid());
+ BOOST_REQUIRE(addr.IsIPv6());
+ BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK_EQUAL(addr.ToString(), link_local);
+
// TORv2
BOOST_REQUIRE(addr.SetSpecial("6hzph5hv6337r6p2.onion"));
BOOST_REQUIRE(addr.IsValid());
BOOST_REQUIRE(addr.IsTor());
BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
// TORv3
@@ -261,6 +325,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsTor());
BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), torv3_addr);
// TORv3, broken, with wrong checksum
@@ -285,6 +350,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_basic)
BOOST_REQUIRE(addr.IsInternal());
BOOST_CHECK(!addr.IsBindAny());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "esffpvrt3wpeaygy.internal");
// Totally bogus
@@ -379,6 +445,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsIPv4());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "1.2.3.4");
BOOST_REQUIRE(s.empty());
@@ -415,6 +482,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsIPv6());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "102:304:506:708:90a:b0c:d0e:f10");
BOOST_REQUIRE(s.empty());
@@ -426,6 +494,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
// sha256(name)[0:10]
s >> addr;
BOOST_CHECK(addr.IsInternal());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "zklycewkdo64v6wc.internal");
BOOST_REQUIRE(s.empty());
@@ -461,6 +530,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsTor());
+ BOOST_CHECK(addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "6hzph5hv6337r6p2.onion");
BOOST_REQUIRE(s.empty());
@@ -482,6 +552,7 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
s >> addr;
BOOST_CHECK(addr.IsValid());
BOOST_CHECK(addr.IsTor());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(),
"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion");
BOOST_REQUIRE(s.empty());
@@ -503,6 +574,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
"f98232ae42d4b6fd2fa81952dfe36a87"));
s >> addr;
BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsI2P());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(),
"ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p");
BOOST_REQUIRE(s.empty());
@@ -524,6 +597,8 @@ BOOST_AUTO_TEST_CASE(cnetaddr_unserialize_v2)
));
s >> addr;
BOOST_CHECK(addr.IsValid());
+ BOOST_CHECK(addr.IsCJDNS());
+ BOOST_CHECK(!addr.IsAddrV1Compatible());
BOOST_CHECK_EQUAL(addr.ToString(), "fc00:1:2:3:4:5:6:7");
BOOST_REQUIRE(s.empty());
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 6681c92bb5..f5d26fafef 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -4,9 +4,13 @@
#include <net_permissions.h>
#include <netbase.h>
+#include <protocol.h>
+#include <serialize.h>
+#include <streams.h>
#include <test/util/setup_common.h>
#include <util/strencodings.h>
#include <util/translation.h>
+#include <version.h>
#include <string>
@@ -443,4 +447,105 @@ BOOST_AUTO_TEST_CASE(netbase_dont_resolve_strings_with_embedded_nul_characters)
BOOST_CHECK(!LookupSubNet(std::string("5wyqrzbvrdsumnok.onion\0example.com\0", 35), ret));
}
+// Since CNetAddr (un)ser is tested separately in net_tests.cpp here we only
+// try a few edge cases for port, service flags and time.
+
+static const std::vector<CAddress> fixture_addresses({
+ CAddress(
+ CService(CNetAddr(in6_addr(IN6ADDR_LOOPBACK_INIT)), 0 /* port */),
+ NODE_NONE,
+ 0x4966bc61U /* Fri Jan 9 02:54:25 UTC 2009 */
+ ),
+ CAddress(
+ CService(CNetAddr(in6_addr(IN6ADDR_LOOPBACK_INIT)), 0x00f1 /* port */),
+ NODE_NETWORK,
+ 0x83766279U /* Tue Nov 22 11:22:33 UTC 2039 */
+ ),
+ CAddress(
+ CService(CNetAddr(in6_addr(IN6ADDR_LOOPBACK_INIT)), 0xf1f2 /* port */),
+ static_cast<ServiceFlags>(NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED),
+ 0xffffffffU /* Sun Feb 7 06:28:15 UTC 2106 */
+ )
+});
+
+// fixture_addresses should equal to this when serialized in V1 format.
+// When this is unserialized from V1 format it should equal to fixture_addresses.
+static constexpr const char* stream_addrv1_hex =
+ "03" // number of entries
+
+ "61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
+ "0000000000000000" // service flags, NODE_NONE
+ "00000000000000000000000000000001" // address, fixed 16 bytes (IPv4 embedded in IPv6)
+ "0000" // port
+
+ "79627683" // time, Tue Nov 22 11:22:33 UTC 2039
+ "0100000000000000" // service flags, NODE_NETWORK
+ "00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
+ "00f1" // port
+
+ "ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
+ "4804000000000000" // service flags, NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED
+ "00000000000000000000000000000001" // address, fixed 16 bytes (IPv6)
+ "f1f2"; // port
+
+// fixture_addresses should equal to this when serialized in V2 format.
+// When this is unserialized from V2 format it should equal to fixture_addresses.
+static constexpr const char* stream_addrv2_hex =
+ "03" // number of entries
+
+ "61bc6649" // time, Fri Jan 9 02:54:25 UTC 2009
+ "00" // service flags, COMPACTSIZE(NODE_NONE)
+ "02" // network id, IPv6
+ "10" // address length, COMPACTSIZE(16)
+ "00000000000000000000000000000001" // address
+ "0000" // port
+
+ "79627683" // time, Tue Nov 22 11:22:33 UTC 2039
+ "01" // service flags, COMPACTSIZE(NODE_NETWORK)
+ "02" // network id, IPv6
+ "10" // address length, COMPACTSIZE(16)
+ "00000000000000000000000000000001" // address
+ "00f1" // port
+
+ "ffffffff" // time, Sun Feb 7 06:28:15 UTC 2106
+ "fd4804" // service flags, COMPACTSIZE(NODE_WITNESS | NODE_COMPACT_FILTERS | NODE_NETWORK_LIMITED)
+ "02" // network id, IPv6
+ "10" // address length, COMPACTSIZE(16)
+ "00000000000000000000000000000001" // address
+ "f1f2"; // port
+
+BOOST_AUTO_TEST_CASE(caddress_serialize_v1)
+{
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION);
+
+ s << fixture_addresses;
+ BOOST_CHECK_EQUAL(HexStr(s), stream_addrv1_hex);
+}
+
+BOOST_AUTO_TEST_CASE(caddress_unserialize_v1)
+{
+ CDataStream s(ParseHex(stream_addrv1_hex), SER_NETWORK, PROTOCOL_VERSION);
+ std::vector<CAddress> addresses_unserialized;
+
+ s >> addresses_unserialized;
+ BOOST_CHECK(fixture_addresses == addresses_unserialized);
+}
+
+BOOST_AUTO_TEST_CASE(caddress_serialize_v2)
+{
+ CDataStream s(SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
+
+ s << fixture_addresses;
+ BOOST_CHECK_EQUAL(HexStr(s), stream_addrv2_hex);
+}
+
+BOOST_AUTO_TEST_CASE(caddress_unserialize_v2)
+{
+ CDataStream s(ParseHex(stream_addrv2_hex), SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ std::vector<CAddress> addresses_unserialized;
+
+ s >> addresses_unserialized;
+ BOOST_CHECK(fixture_addresses == addresses_unserialized);
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp
index ca49b89ad8..1d7f4861fb 100644
--- a/src/test/pow_tests.cpp
+++ b/src/test/pow_tests.cpp
@@ -14,7 +14,7 @@ BOOST_FIXTURE_TEST_SUITE(pow_tests, BasicTestingSetup)
/* Test calculation of next difficulty target with no constraints applying */
BOOST_AUTO_TEST_CASE(get_next_work)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1261130161; // Block #30240
CBlockIndex pindexLast;
pindexLast.nHeight = 32255;
@@ -26,7 +26,7 @@ BOOST_AUTO_TEST_CASE(get_next_work)
/* Test the constraint on the upper bound for next work */
BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1231006505; // Block #0
CBlockIndex pindexLast;
pindexLast.nHeight = 2015;
@@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_pow_limit)
/* Test the constraint on the lower bound for actual time taken */
BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1279008237; // Block #66528
CBlockIndex pindexLast;
pindexLast.nHeight = 68543;
@@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_lower_limit_actual)
/* Test the constraint on the upper bound for actual time taken */
BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
int64_t nLastRetargetTime = 1263163443; // NOTE: Not an actual block time
CBlockIndex pindexLast;
pindexLast.nHeight = 46367;
@@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(get_next_work_upper_limit_actual)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
nBits = UintToArith256(consensus.powLimit).GetCompact(true);
@@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_negative_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits = ~0x00800000;
hash.SetHex("0x1");
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_overflow_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_too_easy_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
arith_uint256 nBits_arith = UintToArith256(consensus.powLimit);
@@ -92,7 +92,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_too_easy_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_biger_hash_than_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
arith_uint256 hash_arith = UintToArith256(consensus.powLimit);
@@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_biger_hash_than_target)
BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_zero_target)
{
- const auto consensus = CreateChainParams(CBaseChainParams::MAIN)->GetConsensus();
+ const auto consensus = CreateChainParams(*m_node.args, CBaseChainParams::MAIN)->GetConsensus();
uint256 hash;
unsigned int nBits;
arith_uint256 hash_arith{0};
@@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(CheckProofOfWork_test_zero_target)
BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
std::vector<CBlockIndex> blocks(10000);
for (int i = 0; i < 10000; i++) {
blocks[i].pprev = i ? &blocks[i - 1] : nullptr;
@@ -135,9 +135,9 @@ BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
}
}
-void sanity_check_chainparams(std::string chainName)
+void sanity_check_chainparams(const ArgsManager& args, std::string chainName)
{
- const auto chainParams = CreateChainParams(chainName);
+ const auto chainParams = CreateChainParams(args, chainName);
const auto consensus = chainParams->GetConsensus();
// hash genesis is correct
@@ -164,22 +164,22 @@ void sanity_check_chainparams(std::string chainName)
BOOST_AUTO_TEST_CASE(ChainParams_MAIN_sanity)
{
- sanity_check_chainparams(CBaseChainParams::MAIN);
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::MAIN);
}
BOOST_AUTO_TEST_CASE(ChainParams_REGTEST_sanity)
{
- sanity_check_chainparams(CBaseChainParams::REGTEST);
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::REGTEST);
}
BOOST_AUTO_TEST_CASE(ChainParams_TESTNET_sanity)
{
- sanity_check_chainparams(CBaseChainParams::TESTNET);
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::TESTNET);
}
BOOST_AUTO_TEST_CASE(ChainParams_SIGNET_sanity)
{
- sanity_check_chainparams(CBaseChainParams::SIGNET);
+ sanity_check_chainparams(*m_node.args, CBaseChainParams::SIGNET);
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp
index f6824a4e5e..856ec6346d 100644
--- a/src/test/script_p2sh_tests.cpp
+++ b/src/test/script_p2sh_tests.cpp
@@ -343,7 +343,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txTo.vin[3].scriptSig << OP_11 << OP_11 << std::vector<unsigned char>(oneAndTwo.begin(), oneAndTwo.end());
txTo.vin[4].scriptSig << std::vector<unsigned char>(fifteenSigops.begin(), fifteenSigops.end());
- BOOST_CHECK(::AreInputsStandard(CTransaction(txTo), coins));
+ BOOST_CHECK(::AreInputsStandard(CTransaction(txTo), coins, false));
// 22 P2SH sigops for all inputs (1 for vin[0], 6 for vin[3], 15 for vin[4]
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txTo), coins), 22U);
@@ -356,7 +356,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txToNonStd1.vin[0].prevout.hash = txFrom.GetHash();
txToNonStd1.vin[0].scriptSig << std::vector<unsigned char>(sixteenSigops.begin(), sixteenSigops.end());
- BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd1), coins));
+ BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd1), coins, false));
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd1), coins), 16U);
CMutableTransaction txToNonStd2;
@@ -368,7 +368,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
txToNonStd2.vin[0].prevout.hash = txFrom.GetHash();
txToNonStd2.vin[0].scriptSig << std::vector<unsigned char>(twentySigops.begin(), twentySigops.end());
- BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd2), coins));
+ BOOST_CHECK(!::AreInputsStandard(CTransaction(txToNonStd2), coins, false));
BOOST_CHECK_EQUAL(GetP2SHSigOpCount(CTransaction(txToNonStd2), coins), 20U);
}
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp
index 0830743d61..25ca171b33 100644
--- a/src/test/script_tests.cpp
+++ b/src/test/script_tests.cpp
@@ -5,10 +5,12 @@
#include <test/data/script_tests.json.h>
#include <core_io.h>
+#include <fs.h>
#include <key.h>
#include <rpc/util.h>
#include <script/script.h>
#include <script/script_error.h>
+#include <script/sigcache.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <streams.h>
@@ -104,18 +106,18 @@ static ScriptErrorDesc script_errors[]={
static std::string FormatScriptError(ScriptError_t err)
{
- for (unsigned int i=0; i<ARRAYLEN(script_errors); ++i)
- if (script_errors[i].err == err)
- return script_errors[i].name;
+ for (const auto& se : script_errors)
+ if (se.err == err)
+ return se.name;
BOOST_ERROR("Unknown scripterror enumeration value, update script_errors in script_tests.cpp.");
return "";
}
-static ScriptError_t ParseScriptError(const std::string &name)
+static ScriptError_t ParseScriptError(const std::string& name)
{
- for (unsigned int i=0; i<ARRAYLEN(script_errors); ++i)
- if (script_errors[i].name == name)
- return script_errors[i].err;
+ for (const auto& se : script_errors)
+ if (se.name == name)
+ return se.err;
BOOST_ERROR("Unknown scripterror \"" << name << "\" in test description");
return SCRIPT_ERR_UNKNOWN_ERROR;
}
@@ -1339,14 +1341,12 @@ BOOST_AUTO_TEST_CASE(script_GetScriptAsm)
BOOST_CHECK_EQUAL(derSig + "83 " + pubKey, ScriptToAsmStr(CScript() << ToByteVector(ParseHex(derSig + "83")) << vchPubKey));
}
-static CScript
-ScriptFromHex(const char* hex)
+static CScript ScriptFromHex(const std::string& str)
{
- std::vector<unsigned char> data = ParseHex(hex);
+ std::vector<unsigned char> data = ParseHex(str);
return CScript(data.begin(), data.end());
}
-
BOOST_AUTO_TEST_CASE(script_FindAndDelete)
{
// Exercise the FindAndDelete functionality
@@ -1470,6 +1470,36 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps)
BOOST_CHECK(!script.HasValidOps());
}
+static CMutableTransaction TxFromHex(const std::string& str)
+{
+ CMutableTransaction tx;
+ VectorReader(SER_DISK, SERIALIZE_TRANSACTION_NO_WITNESS, ParseHex(str), 0) >> tx;
+ return tx;
+}
+
+static std::vector<CTxOut> TxOutsFromJSON(const UniValue& univalue)
+{
+ assert(univalue.isArray());
+ std::vector<CTxOut> prevouts;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ CTxOut txout;
+ VectorReader(SER_DISK, 0, ParseHex(univalue[i].get_str()), 0) >> txout;
+ prevouts.push_back(std::move(txout));
+ }
+ return prevouts;
+}
+
+static CScriptWitness ScriptWitnessFromJSON(const UniValue& univalue)
+{
+ assert(univalue.isArray());
+ CScriptWitness scriptwitness;
+ for (size_t i = 0; i < univalue.size(); ++i) {
+ auto bytes = ParseHex(univalue[i].get_str());
+ scriptwitness.stack.push_back(std::move(bytes));
+ }
+ return scriptwitness;
+}
+
#if defined(HAVE_CONSENSUS_LIB)
/* Test simple (successful) usage of bitcoinconsensus_verify_script */
@@ -1610,5 +1640,108 @@ BOOST_AUTO_TEST_CASE(bitcoinconsensus_verify_script_invalid_flags)
BOOST_CHECK_EQUAL(err, bitcoinconsensus_ERR_INVALID_FLAGS);
}
-#endif
+#endif // defined(HAVE_CONSENSUS_LIB)
+
+static std::vector<unsigned int> AllConsensusFlags()
+{
+ std::vector<unsigned int> ret;
+
+ for (unsigned int i = 0; i < 128; ++i) {
+ unsigned int flag = 0;
+ if (i & 1) flag |= SCRIPT_VERIFY_P2SH;
+ if (i & 2) flag |= SCRIPT_VERIFY_DERSIG;
+ if (i & 4) flag |= SCRIPT_VERIFY_NULLDUMMY;
+ if (i & 8) flag |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
+ if (i & 16) flag |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY;
+ if (i & 32) flag |= SCRIPT_VERIFY_WITNESS;
+ if (i & 64) flag |= SCRIPT_VERIFY_TAPROOT;
+
+ // SCRIPT_VERIFY_WITNESS requires SCRIPT_VERIFY_P2SH
+ if (flag & SCRIPT_VERIFY_WITNESS && !(flag & SCRIPT_VERIFY_P2SH)) continue;
+ // SCRIPT_VERIFY_TAPROOT requires SCRIPT_VERIFY_WITNESS
+ if (flag & SCRIPT_VERIFY_TAPROOT && !(flag & SCRIPT_VERIFY_WITNESS)) continue;
+
+ ret.push_back(flag);
+ }
+
+ return ret;
+}
+
+/** Precomputed list of all valid combinations of consensus-relevant script validation flags. */
+static const std::vector<unsigned int> ALL_CONSENSUS_FLAGS = AllConsensusFlags();
+
+static void AssetTest(const UniValue& test)
+{
+ BOOST_CHECK(test.isObject());
+
+ CMutableTransaction mtx = TxFromHex(test["tx"].get_str());
+ const std::vector<CTxOut> prevouts = TxOutsFromJSON(test["prevouts"]);
+ BOOST_CHECK(prevouts.size() == mtx.vin.size());
+ size_t idx = test["index"].get_int64();
+ unsigned int test_flags = ParseScriptFlags(test["flags"].get_str());
+ bool fin = test.exists("final") && test["final"].get_bool();
+
+ if (test.exists("success")) {
+ mtx.vin[idx].scriptSig = ScriptFromHex(test["success"]["scriptSig"].get_str());
+ mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
+ CTransaction tx(mtx);
+ PrecomputedTransactionData txdata;
+ txdata.Init(tx, std::vector<CTxOut>(prevouts));
+ CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
+ for (const auto flags : ALL_CONSENSUS_FLAGS) {
+ // "final": true tests are valid for all flags. Others are only valid with flags that are
+ // a subset of test_flags.
+ if (fin || ((flags & test_flags) == flags)) {
+ bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
+ BOOST_CHECK(ret);
+ }
+ }
+ }
+
+ if (test.exists("failure")) {
+ mtx.vin[idx].scriptSig = ScriptFromHex(test["failure"]["scriptSig"].get_str());
+ mtx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
+ CTransaction tx(mtx);
+ PrecomputedTransactionData txdata;
+ txdata.Init(tx, std::vector<CTxOut>(prevouts));
+ CachingTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, true, txdata);
+ for (const auto flags : ALL_CONSENSUS_FLAGS) {
+ // If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
+ if ((flags & test_flags) == test_flags) {
+ bool ret = VerifyScript(tx.vin[idx].scriptSig, prevouts[idx].scriptPubKey, &tx.vin[idx].scriptWitness, flags, txcheck, nullptr);
+ BOOST_CHECK(!ret);
+ }
+ }
+ }
+}
+
+BOOST_AUTO_TEST_CASE(script_assets_test)
+{
+ // See src/test/fuzz/script_assets_test_minimizer.cpp for information on how to generate
+ // the script_assets_test.json file used by this test.
+
+ const char* dir = std::getenv("DIR_UNIT_TEST_DATA");
+ BOOST_WARN_MESSAGE(dir != nullptr, "Variable DIR_UNIT_TEST_DATA unset, skipping script_assets_test");
+ if (dir == nullptr) return;
+ auto path = fs::path(dir) / "script_assets_test.json";
+ bool exists = fs::exists(path);
+ BOOST_WARN_MESSAGE(exists, "File $DIR_UNIT_TEST_DATA/script_assets_test.json not found, skipping script_assets_test");
+ if (!exists) return;
+ fs::ifstream file(path);
+ BOOST_CHECK(file.is_open());
+ file.seekg(0, std::ios::end);
+ size_t length = file.tellg();
+ file.seekg(0, std::ios::beg);
+ std::string data(length, '\0');
+ file.read(&data[0], data.size());
+ UniValue tests = read_json(data);
+ BOOST_CHECK(tests.isArray());
+ BOOST_CHECK(tests.size() > 0);
+
+ for (size_t i = 0; i < tests.size(); i++) {
+ AssetTest(tests[i]);
+ }
+ file.close();
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 94b5dba913..1f520074b1 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -57,6 +57,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
{std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM},
{std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
{std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
+ {std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
};
unsigned int ParseScriptFlags(std::string strFlags)
@@ -304,7 +305,7 @@ BOOST_AUTO_TEST_CASE(test_Get)
t1.vout[0].nValue = 90*CENT;
t1.vout[0].scriptPubKey << OP_1;
- BOOST_CHECK(AreInputsStandard(CTransaction(t1), coins));
+ BOOST_CHECK(AreInputsStandard(CTransaction(t1), coins, false));
}
static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
diff --git a/src/test/txrequest_tests.cpp b/src/test/txrequest_tests.cpp
new file mode 100644
index 0000000000..1d137b03b1
--- /dev/null
+++ b/src/test/txrequest_tests.cpp
@@ -0,0 +1,738 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+
+#include <txrequest.h>
+#include <uint256.h>
+
+#include <test/util/setup_common.h>
+
+#include <algorithm>
+#include <functional>
+#include <vector>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(txrequest_tests, BasicTestingSetup)
+
+namespace {
+
+constexpr std::chrono::microseconds MIN_TIME = std::chrono::microseconds::min();
+constexpr std::chrono::microseconds MAX_TIME = std::chrono::microseconds::max();
+constexpr std::chrono::microseconds MICROSECOND = std::chrono::microseconds{1};
+constexpr std::chrono::microseconds NO_TIME = std::chrono::microseconds{0};
+
+/** An Action is a function to call at a particular (simulated) timestamp. */
+using Action = std::pair<std::chrono::microseconds, std::function<void()>>;
+
+/** Object that stores actions from multiple interleaved scenarios, and data shared across them.
+ *
+ * The Scenario below is used to fill this.
+ */
+struct Runner
+{
+ /** The TxRequestTracker being tested. */
+ TxRequestTracker txrequest;
+
+ /** List of actions to be executed (in order of increasing timestamp). */
+ std::vector<Action> actions;
+
+ /** Which node ids have been assigned already (to prevent reuse). */
+ std::set<NodeId> peerset;
+
+ /** Which txhashes have been assigned already (to prevent reuse). */
+ std::set<uint256> txhashset;
+
+ /** Which (peer, gtxid) combinations are known to be expired. These need to be accumulated here instead of
+ * checked directly in the GetRequestable return value to avoid introducing a dependency between the various
+ * parallel tests. */
+ std::multiset<std::pair<NodeId, GenTxid>> expired;
+};
+
+std::chrono::microseconds RandomTime8s() { return std::chrono::microseconds{1 + InsecureRandBits(23)}; }
+std::chrono::microseconds RandomTime1y() { return std::chrono::microseconds{1 + InsecureRandBits(45)}; }
+
+/** A proxy for a Runner that helps build a sequence of consecutive test actions on a TxRequestTracker.
+ *
+ * Each Scenario is a proxy through which actions for the (sequential) execution of various tests are added to a
+ * Runner. The actions from multiple scenarios are then run concurrently, resulting in these tests being performed
+ * against a TxRequestTracker in parallel. Every test has its own unique txhashes and NodeIds which are not
+ * reused in other tests, and thus they should be independent from each other. Running them in parallel however
+ * means that we verify the behavior (w.r.t. one test's txhashes and NodeIds) even when the state of the data
+ * structure is more complicated due to the presence of other tests.
+ */
+class Scenario
+{
+ Runner& m_runner;
+ std::chrono::microseconds m_now;
+ std::string m_testname;
+
+public:
+ Scenario(Runner& runner, std::chrono::microseconds starttime) : m_runner(runner), m_now(starttime) {}
+
+ /** Set a name for the current test, to give more clear error messages. */
+ void SetTestName(std::string testname)
+ {
+ m_testname = std::move(testname);
+ }
+
+ /** Advance this Scenario's time; this affects the timestamps newly scheduled events get. */
+ void AdvanceTime(std::chrono::microseconds amount)
+ {
+ assert(amount.count() >= 0);
+ m_now += amount;
+ }
+
+ /** Schedule a ForgetTxHash call at the Scheduler's current time. */
+ void ForgetTxHash(const uint256& txhash)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.ForgetTxHash(txhash);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule a ReceivedInv call at the Scheduler's current time. */
+ void ReceivedInv(NodeId peer, const GenTxid& gtxid, bool pref, std::chrono::microseconds reqtime)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.ReceivedInv(peer, gtxid, pref, reqtime);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule a DisconnectedPeer call at the Scheduler's current time. */
+ void DisconnectedPeer(NodeId peer)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.DisconnectedPeer(peer);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule a RequestedTx call at the Scheduler's current time. */
+ void RequestedTx(NodeId peer, const uint256& txhash, std::chrono::microseconds exptime)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.RequestedTx(peer, txhash, exptime);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule a ReceivedResponse call at the Scheduler's current time. */
+ void ReceivedResponse(NodeId peer, const uint256& txhash)
+ {
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ runner.txrequest.ReceivedResponse(peer, txhash);
+ runner.txrequest.SanityCheck();
+ });
+ }
+
+ /** Schedule calls to verify the TxRequestTracker's state at the Scheduler's current time.
+ *
+ * @param peer The peer whose state will be inspected.
+ * @param expected The expected return value for GetRequestable(peer)
+ * @param candidates The expected return value CountCandidates(peer)
+ * @param inflight The expected return value CountInFlight(peer)
+ * @param completed The expected return value of Count(peer), minus candidates and inflight.
+ * @param checkname An arbitrary string to include in error messages, for test identificatrion.
+ * @param offset Offset with the current time to use (must be <= 0). This allows simulations of time going
+ * backwards (but note that the ordering of this event only follows the scenario's m_now.
+ */
+ void Check(NodeId peer, const std::vector<GenTxid>& expected, size_t candidates, size_t inflight,
+ size_t completed, const std::string& checkname,
+ std::chrono::microseconds offset = std::chrono::microseconds{0})
+ {
+ const auto comment = m_testname + " " + checkname;
+ auto& runner = m_runner;
+ const auto now = m_now;
+ assert(offset.count() <= 0);
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ std::vector<std::pair<NodeId, GenTxid>> expired_now;
+ auto ret = runner.txrequest.GetRequestable(peer, now + offset, &expired_now);
+ for (const auto& entry : expired_now) runner.expired.insert(entry);
+ runner.txrequest.SanityCheck();
+ runner.txrequest.PostGetRequestableSanityCheck(now + offset);
+ size_t total = candidates + inflight + completed;
+ size_t real_total = runner.txrequest.Count(peer);
+ size_t real_candidates = runner.txrequest.CountCandidates(peer);
+ size_t real_inflight = runner.txrequest.CountInFlight(peer);
+ BOOST_CHECK_MESSAGE(real_total == total, strprintf("[" + comment + "] total %i (%i expected)", real_total, total));
+ BOOST_CHECK_MESSAGE(real_inflight == inflight, strprintf("[" + comment + "] inflight %i (%i expected)", real_inflight, inflight));
+ BOOST_CHECK_MESSAGE(real_candidates == candidates, strprintf("[" + comment + "] candidates %i (%i expected)", real_candidates, candidates));
+ BOOST_CHECK_MESSAGE(ret == expected, "[" + comment + "] mismatching requestables");
+ });
+ }
+
+ /** Verify that an announcement for gtxid by peer has expired some time before this check is scheduled.
+ *
+ * Every expected expiration should be accounted for through exactly one call to this function.
+ */
+ void CheckExpired(NodeId peer, GenTxid gtxid)
+ {
+ const auto& testname = m_testname;
+ auto& runner = m_runner;
+ runner.actions.emplace_back(m_now, [=,&runner]() {
+ auto it = runner.expired.find(std::pair<NodeId, GenTxid>{peer, gtxid});
+ BOOST_CHECK_MESSAGE(it != runner.expired.end(), "[" + testname + "] missing expiration");
+ if (it != runner.expired.end()) runner.expired.erase(it);
+ });
+ }
+
+ /** Generate a random txhash, whose priorities for certain peers are constrained.
+ *
+ * For example, NewTxHash({{p1,p2,p3},{p2,p4,p5}}) will generate a txhash T such that both:
+ * - priority(p1,T) > priority(p2,T) > priority(p3,T)
+ * - priority(p2,T) > priority(p4,T) > priority(p5,T)
+ * where priority is the predicted internal TxRequestTracker's priority, assuming all announcements
+ * are within the same preferredness class.
+ */
+ uint256 NewTxHash(const std::vector<std::vector<NodeId>>& orders = {})
+ {
+ uint256 ret;
+ bool ok;
+ do {
+ ret = InsecureRand256();
+ ok = true;
+ for (const auto& order : orders) {
+ for (size_t pos = 1; pos < order.size(); ++pos) {
+ uint64_t prio_prev = m_runner.txrequest.ComputePriority(ret, order[pos - 1], true);
+ uint64_t prio_cur = m_runner.txrequest.ComputePriority(ret, order[pos], true);
+ if (prio_prev <= prio_cur) {
+ ok = false;
+ break;
+ }
+ }
+ if (!ok) break;
+ }
+ if (ok) {
+ ok = m_runner.txhashset.insert(ret).second;
+ }
+ } while(!ok);
+ return ret;
+ }
+
+ /** Generate a random GenTxid; the txhash follows NewTxHash; the is_wtxid flag is random. */
+ GenTxid NewGTxid(const std::vector<std::vector<NodeId>>& orders = {})
+ {
+ return {InsecureRandBool(), NewTxHash(orders)};
+ }
+
+ /** Generate a new random NodeId to use as peer. The same NodeId is never returned twice
+ * (across all Scenarios combined). */
+ NodeId NewPeer()
+ {
+ bool ok;
+ NodeId ret;
+ do {
+ ret = InsecureRandBits(63);
+ ok = m_runner.peerset.insert(ret).second;
+ } while(!ok);
+ return ret;
+ }
+
+ std::chrono::microseconds Now() const { return m_now; }
+};
+
+/** Add to scenario a test with a single tx announced by a single peer.
+ *
+ * config is an integer in [0, 32), which controls which variant of the test is used.
+ */
+void BuildSingleTest(Scenario& scenario, int config)
+{
+ auto peer = scenario.NewPeer();
+ auto gtxid = scenario.NewGTxid();
+ bool immediate = config & 1;
+ bool preferred = config & 2;
+ auto delay = immediate ? NO_TIME : RandomTime8s();
+
+ scenario.SetTestName(strprintf("Single(config=%i)", config));
+
+ // Receive an announcement, either immediately requestable or delayed.
+ scenario.ReceivedInv(peer, gtxid, preferred, immediate ? MIN_TIME : scenario.Now() + delay);
+ if (immediate) {
+ scenario.Check(peer, {gtxid}, 1, 0, 0, "s1");
+ } else {
+ scenario.Check(peer, {}, 1, 0, 0, "s2");
+ scenario.AdvanceTime(delay - MICROSECOND);
+ scenario.Check(peer, {}, 1, 0, 0, "s3");
+ scenario.AdvanceTime(MICROSECOND);
+ scenario.Check(peer, {gtxid}, 1, 0, 0, "s4");
+ }
+
+ if (config >> 3) { // We'll request the transaction
+ scenario.AdvanceTime(RandomTime8s());
+ auto expiry = RandomTime8s();
+ scenario.Check(peer, {gtxid}, 1, 0, 0, "s5");
+ scenario.RequestedTx(peer, gtxid.GetHash(), scenario.Now() + expiry);
+ scenario.Check(peer, {}, 0, 1, 0, "s6");
+
+ if ((config >> 3) == 1) { // The request will time out
+ scenario.AdvanceTime(expiry - MICROSECOND);
+ scenario.Check(peer, {}, 0, 1, 0, "s7");
+ scenario.AdvanceTime(MICROSECOND);
+ scenario.Check(peer, {}, 0, 0, 0, "s8");
+ scenario.CheckExpired(peer, gtxid);
+ return;
+ } else {
+ scenario.AdvanceTime(std::chrono::microseconds{InsecureRandRange(expiry.count())});
+ scenario.Check(peer, {}, 0, 1, 0, "s9");
+ if ((config >> 3) == 3) { // A response will arrive for the transaction
+ scenario.ReceivedResponse(peer, gtxid.GetHash());
+ scenario.Check(peer, {}, 0, 0, 0, "s10");
+ return;
+ }
+ }
+ }
+
+ if (config & 4) { // The peer will go offline
+ scenario.DisconnectedPeer(peer);
+ } else { // The transaction is no longer needed
+ scenario.ForgetTxHash(gtxid.GetHash());
+ }
+ scenario.Check(peer, {}, 0, 0, 0, "s11");
+}
+
+/** Add to scenario a test with a single tx announced by two peers, to verify the
+ * right peer is selected for requests.
+ *
+ * config is an integer in [0, 32), which controls which variant of the test is used.
+ */
+void BuildPriorityTest(Scenario& scenario, int config)
+{
+ scenario.SetTestName(strprintf("Priority(config=%i)", config));
+
+ // Two peers. They will announce in order {peer1, peer2}.
+ auto peer1 = scenario.NewPeer(), peer2 = scenario.NewPeer();
+ // Construct a transaction that under random rules would be preferred by peer2 or peer1,
+ // depending on configuration.
+ bool prio1 = config & 1;
+ auto gtxid = prio1 ? scenario.NewGTxid({{peer1, peer2}}) : scenario.NewGTxid({{peer2, peer1}});
+ bool pref1 = config & 2, pref2 = config & 4;
+
+ scenario.ReceivedInv(peer1, gtxid, pref1, MIN_TIME);
+ scenario.Check(peer1, {gtxid}, 1, 0, 0, "p1");
+ if (InsecureRandBool()) {
+ scenario.AdvanceTime(RandomTime8s());
+ scenario.Check(peer1, {gtxid}, 1, 0, 0, "p2");
+ }
+
+ scenario.ReceivedInv(peer2, gtxid, pref2, MIN_TIME);
+ bool stage2_prio =
+ // At this point, peer2 will be given priority if:
+ // - It is preferred and peer1 is not
+ (pref2 && !pref1) ||
+ // - They're in the same preference class,
+ // and the randomized priority favors peer2 over peer1.
+ (pref1 == pref2 && !prio1);
+ NodeId priopeer = stage2_prio ? peer2 : peer1, otherpeer = stage2_prio ? peer1 : peer2;
+ scenario.Check(otherpeer, {}, 1, 0, 0, "p3");
+ scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p4");
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.Check(otherpeer, {}, 1, 0, 0, "p5");
+ scenario.Check(priopeer, {gtxid}, 1, 0, 0, "p6");
+
+ // We possibly request from the selected peer.
+ if (config & 8) {
+ scenario.RequestedTx(priopeer, gtxid.GetHash(), MAX_TIME);
+ scenario.Check(priopeer, {}, 0, 1, 0, "p7");
+ scenario.Check(otherpeer, {}, 1, 0, 0, "p8");
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ }
+
+ // The peer which was selected (or requested from) now goes offline, or a NOTFOUND is received from them.
+ if (config & 16) {
+ scenario.DisconnectedPeer(priopeer);
+ } else {
+ scenario.ReceivedResponse(priopeer, gtxid.GetHash());
+ }
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.Check(priopeer, {}, 0, 0, !(config & 16), "p8");
+ scenario.Check(otherpeer, {gtxid}, 1, 0, 0, "p9");
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+
+ // Now the other peer goes offline.
+ scenario.DisconnectedPeer(otherpeer);
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.Check(peer1, {}, 0, 0, 0, "p10");
+ scenario.Check(peer2, {}, 0, 0, 0, "p11");
+}
+
+/** Add to scenario a randomized test in which N peers announce the same transaction, to verify
+ * the order in which they are requested. */
+void BuildBigPriorityTest(Scenario& scenario, int peers)
+{
+ scenario.SetTestName(strprintf("BigPriority(peers=%i)", peers));
+
+ // We will have N peers announce the same transaction.
+ std::map<NodeId, bool> preferred;
+ std::vector<NodeId> pref_peers, npref_peers;
+ int num_pref = InsecureRandRange(peers + 1) ; // Some preferred, ...
+ int num_npref = peers - num_pref; // some not preferred.
+ for (int i = 0; i < num_pref; ++i) {
+ pref_peers.push_back(scenario.NewPeer());
+ preferred[pref_peers.back()] = true;
+ }
+ for (int i = 0; i < num_npref; ++i) {
+ npref_peers.push_back(scenario.NewPeer());
+ preferred[npref_peers.back()] = false;
+ }
+ // Make a list of all peers, in order of intended request order (concatenation of pref_peers and npref_peers).
+ std::vector<NodeId> request_order;
+ for (int i = 0; i < num_pref; ++i) request_order.push_back(pref_peers[i]);
+ for (int i = 0; i < num_npref; ++i) request_order.push_back(npref_peers[i]);
+
+ // Determine the announcement order randomly.
+ std::vector<NodeId> announce_order = request_order;
+ Shuffle(announce_order.begin(), announce_order.end(), g_insecure_rand_ctx);
+
+ // Find a gtxid whose txhash prioritization is consistent with the required ordering within pref_peers and
+ // within npref_peers.
+ auto gtxid = scenario.NewGTxid({pref_peers, npref_peers});
+
+ // Decide reqtimes in opposite order of the expected request order. This means that as time passes we expect the
+ // to-be-requested-from-peer will change every time a subsequent reqtime is passed.
+ std::map<NodeId, std::chrono::microseconds> reqtimes;
+ auto reqtime = scenario.Now();
+ for (int i = peers - 1; i >= 0; --i) {
+ reqtime += RandomTime8s();
+ reqtimes[request_order[i]] = reqtime;
+ }
+
+ // Actually announce from all peers simultaneously (but in announce_order).
+ for (const auto peer : announce_order) {
+ scenario.ReceivedInv(peer, gtxid, preferred[peer], reqtimes[peer]);
+ }
+ for (const auto peer : announce_order) {
+ scenario.Check(peer, {}, 1, 0, 0, "b1");
+ }
+
+ // Let time pass and observe the to-be-requested-from peer change, from nonpreferred to preferred, and from
+ // high priority to low priority within each class.
+ for (int i = peers - 1; i >= 0; --i) {
+ scenario.AdvanceTime(reqtimes[request_order[i]] - scenario.Now() - MICROSECOND);
+ scenario.Check(request_order[i], {}, 1, 0, 0, "b2");
+ scenario.AdvanceTime(MICROSECOND);
+ scenario.Check(request_order[i], {gtxid}, 1, 0, 0, "b3");
+ }
+
+ // Peers now in random order go offline, or send NOTFOUNDs. At every point in time the new to-be-requested-from
+ // peer should be the best remaining one, so verify this after every response.
+ for (int i = 0; i < peers; ++i) {
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ const int pos = InsecureRandRange(request_order.size());
+ const auto peer = request_order[pos];
+ request_order.erase(request_order.begin() + pos);
+ if (InsecureRandBool()) {
+ scenario.DisconnectedPeer(peer);
+ scenario.Check(peer, {}, 0, 0, 0, "b4");
+ } else {
+ scenario.ReceivedResponse(peer, gtxid.GetHash());
+ scenario.Check(peer, {}, 0, 0, request_order.size() > 0, "b5");
+ }
+ if (request_order.size()) {
+ scenario.Check(request_order[0], {gtxid}, 1, 0, 0, "b6");
+ }
+ }
+
+ // Everything is gone in the end.
+ for (const auto peer : announce_order) {
+ scenario.Check(peer, {}, 0, 0, 0, "b7");
+ }
+}
+
+/** Add to scenario a test with one peer announcing two transactions, to verify they are
+ * fetched in announcement order.
+ *
+ * config is an integer in [0, 4) inclusive, and selects the variant of the test.
+ */
+void BuildRequestOrderTest(Scenario& scenario, int config)
+{
+ scenario.SetTestName(strprintf("RequestOrder(config=%i)", config));
+
+ auto peer = scenario.NewPeer();
+ auto gtxid1 = scenario.NewGTxid();
+ auto gtxid2 = scenario.NewGTxid();
+
+ auto reqtime2 = scenario.Now() + RandomTime8s();
+ auto reqtime1 = reqtime2 + RandomTime8s();
+
+ scenario.ReceivedInv(peer, gtxid1, config & 1, reqtime1);
+ // Simulate time going backwards by giving the second announcement an earlier reqtime.
+ scenario.ReceivedInv(peer, gtxid2, config & 2, reqtime2);
+
+ scenario.AdvanceTime(reqtime2 - MICROSECOND - scenario.Now());
+ scenario.Check(peer, {}, 2, 0, 0, "o1");
+ scenario.AdvanceTime(MICROSECOND);
+ scenario.Check(peer, {gtxid2}, 2, 0, 0, "o2");
+ scenario.AdvanceTime(reqtime1 - MICROSECOND - scenario.Now());
+ scenario.Check(peer, {gtxid2}, 2, 0, 0, "o3");
+ scenario.AdvanceTime(MICROSECOND);
+ // Even with time going backwards in between announcements, the return value of GetRequestable is in
+ // announcement order.
+ scenario.Check(peer, {gtxid1, gtxid2}, 2, 0, 0, "o4");
+
+ scenario.DisconnectedPeer(peer);
+ scenario.Check(peer, {}, 0, 0, 0, "o5");
+}
+
+/** Add to scenario a test that verifies behavior related to both txid and wtxid with the same
+ * hash being announced.
+ *
+ * config is an integer in [0, 4) inclusive, and selects the variant of the test used.
+*/
+void BuildWtxidTest(Scenario& scenario, int config)
+{
+ scenario.SetTestName(strprintf("Wtxid(config=%i)", config));
+
+ auto peerT = scenario.NewPeer();
+ auto peerW = scenario.NewPeer();
+ auto txhash = scenario.NewTxHash();
+ GenTxid txid{false, txhash};
+ GenTxid wtxid{true, txhash};
+
+ auto reqtimeT = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
+ auto reqtimeW = InsecureRandBool() ? MIN_TIME : scenario.Now() + RandomTime8s();
+
+ // Announce txid first or wtxid first.
+ if (config & 1) {
+ scenario.ReceivedInv(peerT, txid, config & 2, reqtimeT);
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peerW, wtxid, !(config & 2), reqtimeW);
+ } else {
+ scenario.ReceivedInv(peerW, wtxid, !(config & 2), reqtimeW);
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peerT, txid, config & 2, reqtimeT);
+ }
+
+ // Let time pass if needed, and check that the preferred announcement (txid or wtxid)
+ // is correctly to-be-requested (and with the correct wtxidness).
+ auto max_reqtime = std::max(reqtimeT, reqtimeW);
+ if (max_reqtime > scenario.Now()) scenario.AdvanceTime(max_reqtime - scenario.Now());
+ if (config & 2) {
+ scenario.Check(peerT, {txid}, 1, 0, 0, "w1");
+ scenario.Check(peerW, {}, 1, 0, 0, "w2");
+ } else {
+ scenario.Check(peerT, {}, 1, 0, 0, "w3");
+ scenario.Check(peerW, {wtxid}, 1, 0, 0, "w4");
+ }
+
+ // Let the preferred announcement be requested. It's not going to be delivered.
+ auto expiry = RandomTime8s();
+ if (config & 2) {
+ scenario.RequestedTx(peerT, txid.GetHash(), scenario.Now() + expiry);
+ scenario.Check(peerT, {}, 0, 1, 0, "w5");
+ scenario.Check(peerW, {}, 1, 0, 0, "w6");
+ } else {
+ scenario.RequestedTx(peerW, wtxid.GetHash(), scenario.Now() + expiry);
+ scenario.Check(peerT, {}, 1, 0, 0, "w7");
+ scenario.Check(peerW, {}, 0, 1, 0, "w8");
+ }
+
+ // After reaching expiration time of the preferred announcement, verify that the
+ // remaining one is requestable
+ scenario.AdvanceTime(expiry);
+ if (config & 2) {
+ scenario.Check(peerT, {}, 0, 0, 1, "w9");
+ scenario.Check(peerW, {wtxid}, 1, 0, 0, "w10");
+ scenario.CheckExpired(peerT, txid);
+ } else {
+ scenario.Check(peerT, {txid}, 1, 0, 0, "w11");
+ scenario.Check(peerW, {}, 0, 0, 1, "w12");
+ scenario.CheckExpired(peerW, wtxid);
+ }
+
+ // If a good transaction with either that hash as wtxid or txid arrives, both
+ // announcements are gone.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ForgetTxHash(txhash);
+ scenario.Check(peerT, {}, 0, 0, 0, "w13");
+ scenario.Check(peerW, {}, 0, 0, 0, "w14");
+}
+
+/** Add to scenario a test that exercises clocks that go backwards. */
+void BuildTimeBackwardsTest(Scenario& scenario)
+{
+ auto peer1 = scenario.NewPeer();
+ auto peer2 = scenario.NewPeer();
+ auto gtxid = scenario.NewGTxid({{peer1, peer2}});
+
+ // Announce from peer2.
+ auto reqtime = scenario.Now() + RandomTime8s();
+ scenario.ReceivedInv(peer2, gtxid, true, reqtime);
+ scenario.Check(peer2, {}, 1, 0, 0, "r1");
+ scenario.AdvanceTime(reqtime - scenario.Now());
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r2");
+ // Check that if the clock goes backwards by 1us, the transaction would stop being requested.
+ scenario.Check(peer2, {}, 1, 0, 0, "r3", -MICROSECOND);
+ // But it reverts to being requested if time goes forward again.
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r4");
+
+ // Announce from peer1.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peer1, gtxid, true, MAX_TIME);
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r5");
+ scenario.Check(peer1, {}, 1, 0, 0, "r6");
+
+ // Request from peer1.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ auto expiry = scenario.Now() + RandomTime8s();
+ scenario.RequestedTx(peer1, gtxid.GetHash(), expiry);
+ scenario.Check(peer1, {}, 0, 1, 0, "r7");
+ scenario.Check(peer2, {}, 1, 0, 0, "r8");
+
+ // Expiration passes.
+ scenario.AdvanceTime(expiry - scenario.Now());
+ scenario.Check(peer1, {}, 0, 0, 1, "r9");
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r10"); // Request goes back to peer2.
+ scenario.CheckExpired(peer1, gtxid);
+ scenario.Check(peer1, {}, 0, 0, 1, "r11", -MICROSECOND); // Going back does not unexpire.
+ scenario.Check(peer2, {gtxid}, 1, 0, 0, "r12", -MICROSECOND);
+
+ // Peer2 goes offline, meaning no viable announcements remain.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.DisconnectedPeer(peer2);
+ scenario.Check(peer1, {}, 0, 0, 0, "r13");
+ scenario.Check(peer2, {}, 0, 0, 0, "r14");
+}
+
+/** Add to scenario a test that involves RequestedTx() calls for txhashes not returned by GetRequestable. */
+void BuildWeirdRequestsTest(Scenario& scenario)
+{
+ auto peer1 = scenario.NewPeer();
+ auto peer2 = scenario.NewPeer();
+ auto gtxid1 = scenario.NewGTxid({{peer1, peer2}});
+ auto gtxid2 = scenario.NewGTxid({{peer2, peer1}});
+
+ // Announce gtxid1 by peer1.
+ scenario.ReceivedInv(peer1, gtxid1, true, MIN_TIME);
+ scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q1");
+
+ // Announce gtxid2 by peer2.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peer2, gtxid2, true, MIN_TIME);
+ scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q2");
+ scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q3");
+
+ // We request gtxid2 from *peer1* - no effect.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.RequestedTx(peer1, gtxid2.GetHash(), MAX_TIME);
+ scenario.Check(peer1, {gtxid1}, 1, 0, 0, "q4");
+ scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q5");
+
+ // Now request gtxid1 from peer1 - marks it as REQUESTED.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ auto expiryA = scenario.Now() + RandomTime8s();
+ scenario.RequestedTx(peer1, gtxid1.GetHash(), expiryA);
+ scenario.Check(peer1, {}, 0, 1, 0, "q6");
+ scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q7");
+
+ // Request it a second time - nothing happens, as it's already REQUESTED.
+ auto expiryB = expiryA + RandomTime8s();
+ scenario.RequestedTx(peer1, gtxid1.GetHash(), expiryB);
+ scenario.Check(peer1, {}, 0, 1, 0, "q8");
+ scenario.Check(peer2, {gtxid2}, 1, 0, 0, "q9");
+
+ // Also announce gtxid1 from peer2 now, so that the txhash isn't forgotten when the peer1 request expires.
+ scenario.ReceivedInv(peer2, gtxid1, true, MIN_TIME);
+ scenario.Check(peer1, {}, 0, 1, 0, "q10");
+ scenario.Check(peer2, {gtxid2}, 2, 0, 0, "q11");
+
+ // When reaching expiryA, it expires (not expiryB, which is later).
+ scenario.AdvanceTime(expiryA - scenario.Now());
+ scenario.Check(peer1, {}, 0, 0, 1, "q12");
+ scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q13");
+ scenario.CheckExpired(peer1, gtxid1);
+
+ // Requesting it yet again from peer1 doesn't do anything, as it's already COMPLETED.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.RequestedTx(peer1, gtxid1.GetHash(), MAX_TIME);
+ scenario.Check(peer1, {}, 0, 0, 1, "q14");
+ scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q15");
+
+ // Now announce gtxid2 from peer1.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.ReceivedInv(peer1, gtxid2, true, MIN_TIME);
+ scenario.Check(peer1, {}, 1, 0, 1, "q16");
+ scenario.Check(peer2, {gtxid2, gtxid1}, 2, 0, 0, "q17");
+
+ // And request it from peer1 (weird as peer2 has the preference).
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.RequestedTx(peer1, gtxid2.GetHash(), MAX_TIME);
+ scenario.Check(peer1, {}, 0, 1, 1, "q18");
+ scenario.Check(peer2, {gtxid1}, 2, 0, 0, "q19");
+
+ // If peer2 now (normally) requests gtxid2, the existing request by peer1 becomes COMPLETED.
+ if (InsecureRandBool()) scenario.AdvanceTime(RandomTime8s());
+ scenario.RequestedTx(peer2, gtxid2.GetHash(), MAX_TIME);
+ scenario.Check(peer1, {}, 0, 0, 2, "q20");
+ scenario.Check(peer2, {gtxid1}, 1, 1, 0, "q21");
+
+ // If peer2 goes offline, no viable announcements remain.
+ scenario.DisconnectedPeer(peer2);
+ scenario.Check(peer1, {}, 0, 0, 0, "q22");
+ scenario.Check(peer2, {}, 0, 0, 0, "q23");
+}
+
+void TestInterleavedScenarios()
+{
+ // Create a list of functions which add tests to scenarios.
+ std::vector<std::function<void(Scenario&)>> builders;
+ // Add instances of every test, for every configuration.
+ for (int n = 0; n < 64; ++n) {
+ builders.emplace_back([n](Scenario& scenario){ BuildWtxidTest(scenario, n); });
+ builders.emplace_back([n](Scenario& scenario){ BuildRequestOrderTest(scenario, n & 3); });
+ builders.emplace_back([n](Scenario& scenario){ BuildSingleTest(scenario, n & 31); });
+ builders.emplace_back([n](Scenario& scenario){ BuildPriorityTest(scenario, n & 31); });
+ builders.emplace_back([n](Scenario& scenario){ BuildBigPriorityTest(scenario, (n & 7) + 1); });
+ builders.emplace_back([](Scenario& scenario){ BuildTimeBackwardsTest(scenario); });
+ builders.emplace_back([](Scenario& scenario){ BuildWeirdRequestsTest(scenario); });
+ }
+ // Randomly shuffle all those functions.
+ Shuffle(builders.begin(), builders.end(), g_insecure_rand_ctx);
+
+ Runner runner;
+ auto starttime = RandomTime1y();
+ // Construct many scenarios, and run (up to) 10 randomly-chosen tests consecutively in each.
+ while (builders.size()) {
+ // Introduce some variation in the start time of each scenario, so they don't all start off
+ // concurrently, but get a more random interleaving.
+ auto scenario_start = starttime + RandomTime8s() + RandomTime8s() + RandomTime8s();
+ Scenario scenario(runner, scenario_start);
+ for (int j = 0; builders.size() && j < 10; ++j) {
+ builders.back()(scenario);
+ builders.pop_back();
+ }
+ }
+ // Sort all the actions from all those scenarios chronologically, resulting in the actions from
+ // distinct scenarios to become interleaved. Use stable_sort so that actions from one scenario
+ // aren't reordered w.r.t. each other.
+ std::stable_sort(runner.actions.begin(), runner.actions.end(), [](const Action& a1, const Action& a2) {
+ return a1.first < a2.first;
+ });
+
+ // Run all actions from all scenarios, in order.
+ for (auto& action : runner.actions) {
+ action.second();
+ }
+
+ BOOST_CHECK_EQUAL(runner.txrequest.Size(), 0U);
+ BOOST_CHECK(runner.expired.empty());
+}
+
+} // namespace
+
+BOOST_AUTO_TEST_CASE(TxRequestTest)
+{
+ for (int i = 0; i < 5; ++i) {
+ TestInterleavedScenarios();
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index c3d7af8323..7e6246d68f 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -40,8 +40,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
false,
AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
nullptr /* plTxnReplaced */,
- true /* bypass_limits */,
- 0 /* nAbsurdFee */));
+ true /* bypass_limits */));
// Check that the transaction hasn't been added to mempool.
BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index 034577aa2c..bed2ba3608 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -30,7 +30,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
TxValidationState state;
return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
- nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
+ nullptr /* plTxnReplaced */, true /* bypass_limits */);
};
// Create a double-spend of mature coinbase txn:
diff --git a/src/test/util/validation.cpp b/src/test/util/validation.cpp
new file mode 100644
index 0000000000..1aed492c3c
--- /dev/null
+++ b/src/test/util/validation.cpp
@@ -0,0 +1,22 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/util/validation.h>
+
+#include <util/check.h>
+#include <util/time.h>
+#include <validation.h>
+
+void TestChainState::ResetIbd()
+{
+ m_cached_finished_ibd = false;
+ assert(IsInitialBlockDownload());
+}
+
+void TestChainState::JumpOutOfIbd()
+{
+ Assert(IsInitialBlockDownload());
+ m_cached_finished_ibd = true;
+ Assert(!IsInitialBlockDownload());
+}
diff --git a/src/test/util/validation.h b/src/test/util/validation.h
new file mode 100644
index 0000000000..b13aa0be60
--- /dev/null
+++ b/src/test/util/validation.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_VALIDATION_H
+#define BITCOIN_TEST_UTIL_VALIDATION_H
+
+#include <validation.h>
+
+struct TestChainState : public CChainState {
+ /** Reset the ibd cache to its initial state */
+ void ResetIbd();
+ /** Toggle IsInitialBlockDownload from true to false */
+ void JumpOutOfIbd();
+};
+
+#endif // BITCOIN_TEST_UTIL_VALIDATION_H
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 241c56934e..010b6adf1f 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -42,6 +42,28 @@ namespace BCLog {
BOOST_FIXTURE_TEST_SUITE(util_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(util_datadir)
+{
+ ClearDatadirCache();
+ const fs::path dd_norm = GetDataDir();
+
+ gArgs.ForceSetArg("-datadir", dd_norm.string() + "/");
+ ClearDatadirCache();
+ BOOST_CHECK_EQUAL(dd_norm, GetDataDir());
+
+ gArgs.ForceSetArg("-datadir", dd_norm.string() + "/.");
+ ClearDatadirCache();
+ BOOST_CHECK_EQUAL(dd_norm, GetDataDir());
+
+ gArgs.ForceSetArg("-datadir", dd_norm.string() + "/./");
+ ClearDatadirCache();
+ BOOST_CHECK_EQUAL(dd_norm, GetDataDir());
+
+ gArgs.ForceSetArg("-datadir", dd_norm.string() + "/.//");
+ ClearDatadirCache();
+ BOOST_CHECK_EQUAL(dd_norm, GetDataDir());
+}
+
BOOST_AUTO_TEST_CASE(util_check)
{
// Check that Assert can forward
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index 8e85b7df3e..ea17cb50f1 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -291,8 +291,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
state,
tx,
&plTxnReplaced,
- /* bypass_limits */ false,
- /* nAbsurdFee */ 0));
+ /* bypass_limits */ false));
}
}
diff --git a/src/test/validation_tests.cpp b/src/test/validation_tests.cpp
index 3b961db52d..c3816af0cd 100644
--- a/src/test/validation_tests.cpp
+++ b/src/test/validation_tests.cpp
@@ -4,11 +4,11 @@
#include <chainparams.h>
#include <net.h>
+#include <signet.h>
#include <validation.h>
#include <test/util/setup_common.h>
-#include <boost/signals2/signal.hpp>
#include <boost/test/unit_test.hpp>
BOOST_FIXTURE_TEST_SUITE(validation_tests, TestingSetup)
@@ -39,7 +39,7 @@ static void TestBlockSubsidyHalvings(int nSubsidyHalvingInterval)
BOOST_AUTO_TEST_CASE(block_subsidy_test)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
TestBlockSubsidyHalvings(chainParams->GetConsensus()); // As in main
TestBlockSubsidyHalvings(150); // As in regtest
TestBlockSubsidyHalvings(1000); // Just another interval
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(block_subsidy_test)
BOOST_AUTO_TEST_CASE(subsidy_limit_test)
{
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
CAmount nSum = 0;
for (int nHeight = 0; nHeight < 14000000; nHeight += 1000) {
CAmount nSubsidy = GetBlockSubsidy(nHeight, chainParams->GetConsensus());
@@ -58,20 +58,65 @@ BOOST_AUTO_TEST_CASE(subsidy_limit_test)
BOOST_CHECK_EQUAL(nSum, CAmount{2099999997690000});
}
-static bool ReturnFalse() { return false; }
-static bool ReturnTrue() { return true; }
-
-BOOST_AUTO_TEST_CASE(test_combiner_all)
+BOOST_AUTO_TEST_CASE(signet_parse_tests)
{
- boost::signals2::signal<bool (), CombinerAll> Test;
- BOOST_CHECK(Test());
- Test.connect(&ReturnFalse);
- BOOST_CHECK(!Test());
- Test.connect(&ReturnTrue);
- BOOST_CHECK(!Test());
- Test.disconnect(&ReturnFalse);
- BOOST_CHECK(Test());
- Test.disconnect(&ReturnTrue);
- BOOST_CHECK(Test());
+ ArgsManager signet_argsman;
+ signet_argsman.ForceSetArg("-signetchallenge", "51"); // set challenge to OP_TRUE
+ const auto signet_params = CreateChainParams(signet_argsman, CBaseChainParams::SIGNET);
+ CBlock block;
+ BOOST_CHECK(signet_params->GetConsensus().signet_challenge == std::vector<uint8_t>{OP_TRUE});
+ CScript challenge{OP_TRUE};
+
+ // empty block is invalid
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // no witness commitment
+ CMutableTransaction cb;
+ cb.vout.emplace_back(0, CScript{});
+ block.vtx.push_back(MakeTransactionRef(cb));
+ block.vtx.push_back(MakeTransactionRef(cb)); // Add dummy tx to excercise merkle root code
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // no header is treated valid
+ std::vector<uint8_t> witness_commitment_section_141{0xaa, 0x21, 0xa9, 0xed};
+ for (int i = 0; i < 32; ++i) {
+ witness_commitment_section_141.push_back(0xff);
+ }
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(SignetTxs::Create(block, challenge));
+ BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // no data after header, valid
+ std::vector<uint8_t> witness_commitment_section_325{0xec, 0xc7, 0xda, 0xa2};
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(SignetTxs::Create(block, challenge));
+ BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // Premature end of data, invalid
+ witness_commitment_section_325.push_back(0x01);
+ witness_commitment_section_325.push_back(0x51);
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // has data, valid
+ witness_commitment_section_325.push_back(0x00);
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(SignetTxs::Create(block, challenge));
+ BOOST_CHECK(CheckSignetBlockSolution(block, signet_params->GetConsensus()));
+
+ // Extraneous data, invalid
+ witness_commitment_section_325.push_back(0x00);
+ cb.vout.at(0).scriptPubKey = CScript{} << OP_RETURN << witness_commitment_section_141 << witness_commitment_section_325;
+ block.vtx.at(0) = MakeTransactionRef(cb);
+ BOOST_CHECK(!SignetTxs::Create(block, challenge));
+ BOOST_CHECK(!CheckSignetBlockSolution(block, signet_params->GetConsensus()));
}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index 11c6bdad91..50444f7bbe 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
}
// Sanity checks of version bit deployments
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
for (int i=0; i<(int) Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
uint32_t bitmask = VersionBitsMask(mainnetParams, static_cast<Consensus::DeploymentPos>(i));
@@ -250,7 +250,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
{
// Check that ComputeBlockVersion will set the appropriate bit correctly
// on mainnet.
- const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
+ const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
// Use the TESTDUMMY deployment for testing purposes.