-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbip69_tests.cpp
More file actions
202 lines (173 loc) · 8.06 KB
/
Copy pathbip69_tests.cpp
File metadata and controls
202 lines (173 loc) · 8.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// Copyright (c) 2022 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/setup_common.h>
#include <primitives/transaction.h>
#include <random.h>
#include <boost/test/unit_test.hpp>
#include <algorithm>
#include <cstring>
#include <vector>
BOOST_FIXTURE_TEST_SUITE(bip69_tests, TestingSetup)
static std::vector<uint8_t> GetRandBytes(const size_t n) {
std::vector <uint8_t> ret;
ret.resize(n);
size_t pos = 0;
while (n - pos > 0) {
const auto h = InsecureRand256();
const size_t n2Copy = std::min<size_t>(h.size(), n - pos);
std::memcpy(ret.data() + pos, h.data(), n2Copy);
pos += n2Copy;
}
return ret;
}
static bool IsTxSorted(const CMutableTransaction &tx) {
// check outputs are sorted ascending according to: nValue, scriptPubKey
for (size_t i = 1; i < tx.vout.size(); ++i) {
auto &a = tx.vout[i-1], &b = tx.vout[i];
if (a.nValue > b.nValue) {
return false;
} else if (a.nValue == b.nValue && a.scriptPubKey == b.scriptPubKey && b.tokenDataPtr < a.tokenDataPtr) {
return false;
} else if (a.nValue == b.nValue && a.scriptPubKey != b.scriptPubKey) {
// Note: we cannot use CScript::operator< (which is really prevector::operator<) since
// it does not order things lexicographically. So, we must do this:
const int cmp = std::memcmp(a.scriptPubKey.data(), b.scriptPubKey.data(),
std::min(a.scriptPubKey.size(), b.scriptPubKey.size()));
if (cmp > 0 || (cmp == 0 && a.scriptPubKey.size() > b.scriptPubKey.size())) {
return false;
}
}
}
// check inputs are sorted ascending according to COutpoint::operator<
for (size_t i = 1; i < tx.vin.size(); ++i) {
auto &a = tx.vin[i-1].prevout, &b = tx.vin[i].prevout;
if (!(a < b || a == b)) {
return false;
}
}
return true;
}
BOOST_AUTO_TEST_CASE(random_tests) {
// Create a txn with completely random inputs, random outputs, sort it, test sorting
{
CMutableTransaction tx;
tx.vin.resize(100);
tx.vout.resize(100);
// Completely random input hashes with random input index [0, 100]
for (auto &in : tx.vin) {
in.prevout = COutPoint(TxId{InsecureRand256()}, GetRand(100));
}
// Completely random output value in range [0, 100] BCH, random scriptPubKey data of random length [0, 32]
// and a random token.
for (auto &out : tx.vout) {
out.nValue = int64_t(GetRand(100)) * COIN;
const auto vec = GetRandBytes(GetRand(32));
out.scriptPubKey.assign(vec.begin(), vec.end());
const auto vec2 = GetRandBytes(GetRand(40));
const bool hasNFT = !vec2.empty() || GetRand(2) == 1;
const bool isMutable = hasNFT && GetRand(2) == 1;
out.tokenDataPtr.emplace(token::Id{InsecureRand256()}, token::SafeAmount::fromIntUnchecked(GetRand(123456)),
token::NFTCommitment{vec2.begin(), vec2.end()},
hasNFT, // has nft
isMutable, // mutable
!isMutable && GetRand(2) == 1); // minting
}
BOOST_CHECK_MESSAGE( ! IsTxSorted(tx), "Tx should not be sorted after random generation");
tx.SortBip69();
BOOST_CHECK_MESSAGE(IsTxSorted(tx), "Tx should now be sorted after calling SortBip69");
}
// Create a txn with random inputs that have all index 0, random output amounts but static spk
{
CMutableTransaction tx;
tx.vin.resize(100);
tx.vout.resize(100);
// Completely random input hashes all with input index 0
for (auto &in : tx.vin) {
in.prevout = COutPoint(TxId{InsecureRand256()}, 0);
}
// Completely random output value in range [0, 100] BCH, static trivial spk
for (auto &out : tx.vout) {
out.nValue = int64_t(GetRand(100)) * COIN;
out.scriptPubKey = CScript() << OP_1 << OP_2 << OP_3 << OP_4 << OP_DROP << OP_DROP << OP_DROP;
}
BOOST_CHECK_MESSAGE( ! IsTxSorted(tx), "Tx should not be sorted after random generation");
tx.SortBip69();
BOOST_CHECK_MESSAGE(IsTxSorted(tx), "Tx should now be sorted after calling SortBip69");
}
// Create a txn with:
// - inputs that all have same hash, but random inputN
// - outputs that pay to random scriptPubKeys of length [0, 32], but all have same value
{
CMutableTransaction tx;
tx.vin.resize(100);
tx.vout.resize(100);
// Static input hash with random input index [0, 10,000)
const TxId theId{InsecureRand256()};
for (auto &in : tx.vin) {
in.prevout = COutPoint(theId, GetRand(10'000));
}
// Completely random output value, random scriptPubKey data of random length [0, 32]
const Amount amt = int64_t(GetRand(1234567890)) * SATOSHI;
for (auto &out : tx.vout) {
out.nValue = amt;
const auto vec = GetRandBytes(GetRand(32));
out.scriptPubKey.assign(vec.begin(), vec.end());
}
BOOST_CHECK_MESSAGE( ! IsTxSorted(tx), "Tx should not be sorted after random generation");
tx.SortBip69();
BOOST_CHECK_MESSAGE(IsTxSorted(tx), "Tx should now be sorted after calling SortBip69");
}
// Create a txn with:
// - inputs that all have same hash, but random inputN
// - outputs that pay to a scriptPubKeys of length [0, 32] that is a subtring of a static scriptPubKey of length 32,
// and all have same value.
{
CMutableTransaction tx;
tx.vin.resize(100);
tx.vout.resize(100);
// Static input hash with random input index [0, 10,000]
const TxId theId{InsecureRand256()};
for (auto &in : tx.vin) {
in.prevout = COutPoint(theId, GetRand(10'000));
}
// Completely random output value, scriptPubKey subscript data of random length [0, 32]
const Amount amt = int64_t(GetRand(1234567890)) * SATOSHI;
const auto vec = GetRandBytes(32);
for (auto &out : tx.vout) {
out.nValue = amt;
// each spk is a randomly-sized subscript of `vec` above
out.scriptPubKey.assign(vec.begin(), vec.begin() + GetRand(uint64_t(vec.size())));
}
BOOST_CHECK_MESSAGE( ! IsTxSorted(tx), "Tx should not be sorted after random generation");
tx.SortBip69();
BOOST_CHECK_MESSAGE(IsTxSorted(tx), "Tx should now be sorted after calling SortBip69");
}
// Create a txn with:
// - inputs that all have same hash, but random inputN
// - outputs that pay to a static scriptPubKey, but with random token NFT that is a prefix of a static byte str
{
CMutableTransaction tx;
tx.vin.resize(100);
tx.vout.resize(100);
// Static input hash with random input index [0, 10,000]
const TxId theId{InsecureRand256()};
for (auto &in : tx.vin) {
in.prevout = COutPoint(theId, GetRand(10'000));
}
// Completely random output value, scriptPubKey subscript data of random length [0, 32]
const Amount amt = int64_t(GetRand(1234567890)) * SATOSHI;
const auto vec = GetRandBytes(32);
for (auto &out : tx.vout) {
out.nValue = amt;
out.scriptPubKey.assign(vec.begin(), vec.end());
out.tokenDataPtr.emplace(token::Id(uint256{vec}), token::SafeAmount::fromIntUnchecked(20),
token::NFTCommitment{vec.begin(), vec.begin() + GetRand(vec.size())},
true, true, true);
}
BOOST_CHECK_MESSAGE( ! IsTxSorted(tx), "Tx should not be sorted after random generation");
tx.SortBip69();
BOOST_CHECK_MESSAGE(IsTxSorted(tx), "Tx should now be sorted after calling SortBip69");
}
}
BOOST_AUTO_TEST_SUITE_END()