Skip to content

Commit e4746d6

Browse files
committed
Merge 914c0ca into merged_master (Bitcoin PR bitcoin/bitcoin#22399)
This PR adds a fuzz test ensuring that all the different destination types round-trip to strings (an important test given that Satoshi managed to mess this up, having two different address types encode the same way..) Anyway we fail this test, since both CNoDestination and NullData encode as empty strings, and then this "decodes" as CNoDestination (and returns an error, but the fuzztest doesn't check that). To make the fuzzer pass, I changed NullData to encode and decode as "null". I think this actually adds some functionality, letting you use "null" as an "address" for sendtoaddress and createrawtransaction, thus burning the coins and fixing #1011...but this was not my intent and we probably want to think a bit more carefully before deliberately supporting this.
2 parents 3bdbe9e + 914c0ca commit e4746d6

6 files changed

Lines changed: 70 additions & 78 deletions

File tree

src/key_io.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,14 @@ class DestinationEncoder
127127
}
128128

129129
std::string operator()(const CNoDestination& no) const { return {}; }
130-
std::string operator()(const NullData& null) const { return {}; }
130+
std::string operator()(const NullData& null) const { return "null"; }
131131
};
132132

133133
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, const bool for_parent, std::string& error_str)
134134
{
135+
// ELEMENTS: special case nulldata as "null"
136+
if (str == "null") return NullData{};
137+
135138
std::vector<unsigned char> data;
136139
size_t pk_size = CPubKey::COMPRESSED_SIZE;
137140
uint160 hash;

src/test/fuzz/integer.cpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
#include <pow.h>
1717
#include <protocol.h>
1818
#include <pubkey.h>
19-
#include <rpc/util.h>
20-
#include <script/signingprovider.h>
2119
#include <script/standard.h>
2220
#include <serialize.h>
2321
#include <streams.h>
@@ -158,20 +156,6 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
158156

159157
const CKeyID key_id{u160};
160158
const CScriptID script_id{u160};
161-
// CTxDestination = CNoDestination ∪ PKHash ∪ ScriptHash ∪ WitnessV0ScriptHash ∪ WitnessV0KeyHash ∪ WitnessUnknown
162-
const PKHash pk_hash{u160};
163-
const ScriptHash script_hash{u160};
164-
const WitnessV0KeyHash witness_v0_key_hash{u160};
165-
const WitnessV0ScriptHash witness_v0_script_hash{u256};
166-
const std::vector<CTxDestination> destinations{pk_hash, script_hash, witness_v0_key_hash, witness_v0_script_hash};
167-
const SigningProvider store;
168-
for (const CTxDestination& destination : destinations) {
169-
(void)DescribeAddress(destination);
170-
(void)EncodeDestination(destination);
171-
(void)GetKeyForDestination(store, destination);
172-
(void)GetScriptForDestination(destination);
173-
(void)IsValidDestination(destination);
174-
}
175159

176160
{
177161
CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);

src/test/fuzz/key_io.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
#include <chainparams.h>
66
#include <key_io.h>
7-
#include <rpc/util.h>
8-
#include <script/signingprovider.h>
9-
#include <script/standard.h>
107
#include <test/fuzz/fuzz.h>
118

129
#include <cassert>
@@ -39,12 +36,4 @@ FUZZ_TARGET_INIT(key_io, initialize_key_io)
3936
if (ext_pub_key.pubkey.size() == CPubKey::COMPRESSED_SIZE) {
4037
assert(ext_pub_key == DecodeExtPubKey(EncodeExtPubKey(ext_pub_key)));
4138
}
42-
43-
const CTxDestination tx_destination = DecodeDestination(random_string);
44-
(void)DescribeAddress(tx_destination);
45-
(void)GetKeyForDestination(/* store */ {}, tx_destination);
46-
(void)GetScriptForDestination(tx_destination);
47-
(void)IsValidDestination(tx_destination);
48-
49-
(void)IsValidDestinationString(random_string);
5039
}

src/test/fuzz/script.cpp

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
#include <compressor.h>
77
#include <core_io.h>
88
#include <core_memusage.h>
9+
#include <key_io.h>
910
#include <policy/policy.h>
1011
#include <pubkey.h>
12+
#include <rpc/util.h>
1113
#include <script/descriptor.h>
1214
#include <script/interpreter.h>
1315
#include <script/script.h>
@@ -184,26 +186,26 @@ FUZZ_TARGET_INIT(script, initialize_script)
184186
}
185187

186188
{
187-
WitnessUnknown witness_unknown_1{};
188-
witness_unknown_1.version = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
189-
const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
190-
witness_unknown_1.length = witness_unknown_program_1.size();
191-
std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown_1.program);
192-
193-
WitnessUnknown witness_unknown_2{};
194-
witness_unknown_2.version = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
195-
const std::vector<uint8_t> witness_unknown_program_2 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
196-
witness_unknown_2.length = witness_unknown_program_2.size();
197-
std::copy(witness_unknown_program_2.begin(), witness_unknown_program_2.end(), witness_unknown_2.program);
198-
199-
(void)(witness_unknown_1 == witness_unknown_2);
200-
(void)(witness_unknown_1 < witness_unknown_2);
201-
}
189+
const CTxDestination tx_destination_1{
190+
fuzzed_data_provider.ConsumeBool() ?
191+
DecodeDestination(fuzzed_data_provider.ConsumeRandomLengthString()) :
192+
ConsumeTxDestination(fuzzed_data_provider)};
193+
const CTxDestination tx_destination_2{ConsumeTxDestination(fuzzed_data_provider)};
194+
const std::string encoded_dest{EncodeDestination(tx_destination_1)};
195+
const UniValue json_dest{DescribeAddress(tx_destination_1)};
196+
Assert(tx_destination_1 == DecodeDestination(encoded_dest));
197+
(void)GetKeyForDestination(/* store */ {}, tx_destination_1);
198+
const CScript dest{GetScriptForDestination(tx_destination_1)};
199+
const bool valid{IsValidDestination(tx_destination_1)};
200+
Assert(dest.empty() != valid);
201+
202+
Assert(valid == IsValidDestinationString(encoded_dest));
202203

203-
{
204-
const CTxDestination tx_destination_1 = ConsumeTxDestination(fuzzed_data_provider);
205-
const CTxDestination tx_destination_2 = ConsumeTxDestination(fuzzed_data_provider);
206-
(void)(tx_destination_1 == tx_destination_2);
207204
(void)(tx_destination_1 < tx_destination_2);
205+
if (tx_destination_1 == tx_destination_2) {
206+
Assert(encoded_dest == EncodeDestination(tx_destination_2));
207+
Assert(json_dest.write() == DescribeAddress(tx_destination_2).write());
208+
Assert(dest == GetScriptForDestination(tx_destination_2));
209+
}
208210
}
209211
}

src/test/fuzz/util.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5+
#include <pubkey.h>
56
#include <test/fuzz/util.h>
67
#include <test/util/script.h>
78
#include <util/rbf.h>
@@ -307,3 +308,44 @@ uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept
307308
}) :
308309
fuzzed_data_provider.ConsumeIntegral<uint32_t>();
309310
}
311+
312+
CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
313+
{
314+
CTxDestination tx_destination;
315+
const size_t call_size{CallOneOf(
316+
fuzzed_data_provider,
317+
[&] {
318+
tx_destination = CNoDestination{};
319+
},
320+
[&] {
321+
tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
322+
},
323+
[&] {
324+
tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
325+
},
326+
[&] {
327+
tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)};
328+
},
329+
[&] {
330+
tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)};
331+
},
332+
[&] {
333+
tx_destination = WitnessV1Taproot{XOnlyPubKey{ConsumeUInt256(fuzzed_data_provider)}};
334+
},
335+
[&] {
336+
WitnessUnknown witness_unknown{};
337+
witness_unknown.version = fuzzed_data_provider.ConsumeIntegralInRange(2, 16);
338+
std::vector<uint8_t> witness_unknown_program_1{fuzzed_data_provider.ConsumeBytes<uint8_t>(40)};
339+
if (witness_unknown_program_1.size() < 2) {
340+
witness_unknown_program_1 = {0, 0};
341+
}
342+
witness_unknown.length = witness_unknown_program_1.size();
343+
std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program);
344+
tx_destination = witness_unknown;
345+
},
346+
[&] {
347+
tx_destination = NullData{};
348+
})};
349+
Assert(call_size == std::variant_size_v<CTxDestination>);
350+
return tx_destination;
351+
}

src/test/fuzz/util.h

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,15 @@
3737
#include <vector>
3838

3939
template <typename... Callables>
40-
void CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables)
40+
size_t CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables)
4141
{
4242
constexpr size_t call_size{sizeof...(callables)};
4343
static_assert(call_size >= 1);
4444
const size_t call_index{fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, call_size - 1)};
4545

4646
size_t i{0};
4747
((i++ == call_index ? callables() : void()), ...);
48+
return call_size;
4849
}
4950

5051
template <typename Collection>
@@ -179,36 +180,7 @@ template <typename WeakEnumType, size_t size>
179180
return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}, setPeginsSpent};
180181
}
181182

182-
[[nodiscard]] inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
183-
{
184-
CTxDestination tx_destination;
185-
CallOneOf(
186-
fuzzed_data_provider,
187-
[&] {
188-
tx_destination = CNoDestination{};
189-
},
190-
[&] {
191-
tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
192-
},
193-
[&] {
194-
tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
195-
},
196-
[&] {
197-
tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)};
198-
},
199-
[&] {
200-
tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)};
201-
},
202-
[&] {
203-
WitnessUnknown witness_unknown{};
204-
witness_unknown.version = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
205-
const std::vector<uint8_t> witness_unknown_program_1 = fuzzed_data_provider.ConsumeBytes<uint8_t>(40);
206-
witness_unknown.length = witness_unknown_program_1.size();
207-
std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program);
208-
tx_destination = witness_unknown;
209-
});
210-
return tx_destination;
211-
}
183+
[[nodiscard]] CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept;
212184

213185
template <typename T>
214186
[[nodiscard]] bool MultiplicationOverflow(const T i, const T j) noexcept

0 commit comments

Comments
 (0)