-
Notifications
You must be signed in to change notification settings - Fork 301
/
policy.cpp
322 lines (283 loc) · 12.5 KB
/
policy.cpp
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Copyright (c) 2019 Bitcoin Association
// Distributed under the Open BSV software license, see the accompanying file LICENSE.
// NOTE: This file is intended to be customised by the end user, and includes
// only local node policy logic
#include "policy/policy.h"
#include "script/script_num.h"
#include "taskcancellation.h"
#include "validation.h"
#include "config.h"
/**
* Check transaction inputs to mitigate two potential denial-of-service attacks:
*
* 1. scriptSigs with extra data stuffed into them, not consumed by scriptPubKey
* (or P2SH script)
* 2. P2SH scripts with a crazy number of expensive CHECKSIG/CHECKMULTISIG
* operations
*
* Why bother? To avoid denial-of-service attacks; an attacker can submit a
* standard HASH... OP_EQUAL transaction, which will get accepted into blocks.
* The redemption script can be anything; an attacker could use a very
* expensive-to-check-upon-redemption script like:
* DUP CHECKSIG DROP ... repeated 100 times... OP_1
*/
bool IsStandard(const Config &config, const CScript &scriptPubKey, int32_t nScriptPubKeyHeight, txnouttype &whichType) {
std::vector<std::vector<uint8_t>> vSolutions;
if (!Solver(scriptPubKey, IsGenesisEnabled(config, nScriptPubKeyHeight), whichType, vSolutions)) {
return false;
}
if (whichType == TX_MULTISIG) {
// we don't require minimal encoding here because Solver method is already checking minimal encoding
int m = CScriptNum(vSolutions.front(), false).getint();
int n = CScriptNum(vSolutions.back(), false).getint();
// Support up to x-of-3 multisig txns as standard
if (n < 1 || n > 3) return false;
if (m < 1 || m > n) return false;
} else if (whichType == TX_NULL_DATA) {
if (!config.GetDataCarrier()) {
return false;
}
}
return whichType != TX_NONSTANDARD;
}
bool IsDustReturnTxn (const CTransaction &tx)
{
return tx.vout.size() == 1
&& tx.vout[0].nValue.GetSatoshis() == 0U
&& IsDustReturnScript(tx.vout[0].scriptPubKey);
}
// Check if a transaction is a consolidation transaction.
// A consolidation transaction is a transaction which reduces the size of the UTXO database to
// an extent that is rewarding enough for the miner to mine the transaction for free.
// However, if a consolidation transaction is donated to the miner, then we do not need to honour the consolidation factor
AnnotatedType<bool> IsFreeConsolidationTxn(const Config &config, const CTransaction &tx, const CCoinsViewCache &inputs, int32_t tipHeight)
{
// Allow disabling free consolidation txns via configuring
// the consolidation factor to zero
if (config.GetMinConsolidationFactor() == 0)
return {false, std::nullopt};
const bool isDonation = IsDustReturnTxn(tx);
const uint64_t factor = isDonation
? tx.vin.size()
: config.GetMinConsolidationFactor();
const int32_t minConf = isDonation
? int32_t(0)
: config.GetMinConfConsolidationInput();
const uint64_t maxSize = config.GetMaxConsolidationInputScriptSize();
const bool stdInputOnly = !config.GetAcceptNonStdConsolidationInput();
if (tx.IsCoinBase())
return {false, std::nullopt};
// The consolidation transaction needs to reduce the count of UTXOS
if (tx.vin.size() < factor * tx.vout.size()) {
// We will make an educated guess about the intentions of the transaction sender.
// If the implied consolidation factor is greater 2 but less than the configured consolidation factor,
// then we will emit a hint.
if (tx.vin.size() > 2 * tx.vout.size()) {
return{
false,
strprintf(
"Consolidation transaction %s has too few inputs in relation to outputs to be free."
" Consolidation factor is: %ld"
" See also configuration parameter -minconsolidationfactor.",
tx.GetId().ToString(),
factor)
};
}
return {false, std::nullopt};
}
// Check all UTXOs are confirmed and prevent spam via big
// scriptSig sizes in the consolidation transaction inputs.
uint64_t sumScriptPubKeySizeOfTxInputs = 0;
for (CTxIn const & u: tx.vin) {
// accept only with many confirmations
const auto& coin = inputs.GetCoinWithScript(u.prevout);
assert(coin.has_value());
const auto coinHeight = coin->GetHeight();
if (minConf > 0 && coinHeight == MEMPOOL_HEIGHT) {
return {false,
strprintf(
"Consolidation transaction %s with input from unconfirmed transaction %s is not free."
" See also configuration parameter -minconsolidationinputmaturity",
tx.GetId().ToString(),
u.prevout.GetTxId().ToString())};
}
int32_t seenConf = tipHeight + 1 - coinHeight;
if (minConf > 0 && coinHeight && seenConf < minConf) { // older versions did not store height
return {false,
strprintf(
"Consolidation transaction %s has input from transaction %s with %ld confirmations,"
" minimum required to be free is: %ld."
" See also configuration parameter -minconsolidationinputmaturity",
tx.GetId().ToString(),
u.prevout.GetTxId().ToString(),
seenConf,
minConf)};
}
// spam detection
if (u.scriptSig.size() > maxSize) {
return {false,
strprintf(
"Consolidation transaction %s has input from transaction %s with too large scriptSig %ld"
" to be free. Maximum is %ld."
" See also configuration parameter -maxconsolidationinputscriptsize",
tx.GetId().ToString(),
u.prevout.GetTxId().ToString(),
u.scriptSig.size(),
maxSize)};
}
// if not acceptnonstdconsolidationinput then check if inputs are standard
// and fail otherwise
txnouttype dummyType;
if (stdInputOnly && !IsStandard(config, coin->GetTxOut().scriptPubKey, coinHeight, dummyType)) {
return {false,
strprintf(
"Consolidation transaction %s has non-standard input from transaction %s and cannot be free."
" See also configuration parameter -acceptnonstdconsolidationinput",
tx.GetId().ToString(),
u.prevout.GetTxId().ToString())};
}
// sum up some script sizes
sumScriptPubKeySizeOfTxInputs += coin->GetTxOut().scriptPubKey.size();
}
// check ratio between sum of tx-scriptPubKeys to sum of parent-scriptPubKeys
uint64_t sumScriptPubKeySizeOfTxOutputs = 0;
for (CTxOut const & o: tx.vout) {
sumScriptPubKeySizeOfTxOutputs += o.scriptPubKey.size();
}
// prevent consolidation transactions that are not advantageous enough for miners
if(sumScriptPubKeySizeOfTxInputs < factor * sumScriptPubKeySizeOfTxOutputs) {
return {false,
strprintf(
"Consolidation transaction %s is not free due to relation between cumulated"
" output to input ScriptPubKey sizes %ld/%ld less than %ld"
" See also documentation for configuration parameter -minconsolidationfactor",
tx.GetId().ToString(),
sumScriptPubKeySizeOfTxOutputs,
sumScriptPubKeySizeOfTxInputs,
factor)};
}
if (isDonation)
return {true, strprintf("free donation transaction: %s", tx.GetId().ToString())};
else
return {true, strprintf("free consolidation transaction: %s", tx.GetId().ToString())};
}
bool IsStandardTx(const Config &config, const CTransaction &tx, int32_t nHeight, std::string &reason) {
if (tx.nVersion > CTransaction::MAX_STANDARD_VERSION || tx.nVersion < 1) {
reason = "version";
return false;
}
// Extremely large transactions with lots of inputs can cost the network
// almost as much to process as they cost the sender in fees, because
// computing signature hashes is O(ninputs*txsize). Limiting transactions
// mitigates CPU exhaustion attacks.
unsigned int sz = tx.GetTotalSize();
if (sz > config.GetMaxTxSize(IsGenesisEnabled(config, nHeight), false)) {
reason = "tx-size";
return false;
}
for (const CTxIn &txin : tx.vin) {
// Biggest 'standard' txin is a 15-of-15 P2SH multisig with compressed
// keys (remember the 520 byte limit on redeemScript size). That works
// out to a (15*(33+1))+3=513 byte redeemScript, 513+1+15*(73+1)+3=1627
// bytes of scriptSig, which we round off to 1650 bytes for some minor
// future-proofing. That's also enough to spend a 20-of-20 CHECKMULTISIG
// scriptPubKey, though such a scriptPubKey is not considered standard.
if (!IsGenesisEnabled(config, nHeight) && txin.scriptSig.size() > 1650) {
reason = "scriptsig-size";
return false;
}
if (!txin.scriptSig.IsPushOnly()) {
reason = "scriptsig-not-pushonly";
return false;
}
}
unsigned int nDataSize = 0;
txnouttype whichType;
bool scriptpubkey = false;
for (const CTxOut &txout : tx.vout) {
if (!::IsStandard(config, txout.scriptPubKey, nHeight, whichType)) {
scriptpubkey = true;
}
if (whichType == TX_NULL_DATA) {
nDataSize += txout.scriptPubKey.size();
} else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
reason = "bare-multisig";
return false;
} else if (txout.IsDust(IsGenesisEnabled(config, nHeight))) {
reason = "dust";
return false;
}
}
// cumulative size of all OP_RETURN txout should be smaller than -datacarriersize
if (nDataSize > config.GetDataCarrierSize()) {
reason = "datacarrier-size-exceeded";
return false;
}
if(scriptpubkey)
{
reason = "scriptpubkey";
return false;
}
return true;
}
std::optional<bool> AreInputsStandard(
const task::CCancellationToken& token,
const Config& config,
const CTransaction& tx,
const CCoinsViewCache &mapInputs,
const int32_t mempoolHeight)
{
if (tx.IsCoinBase()) {
// Coinbases don't use vin normally.
return true;
}
for (size_t i = 0; i < tx.vin.size(); i++) {
auto prev = mapInputs.GetCoinWithScript( tx.vin[i].prevout );
assert(prev.has_value());
assert(!prev->IsSpent());
std::vector<std::vector<uint8_t>> vSolutions;
txnouttype whichType;
// get the scriptPubKey corresponding to this input:
const CScript &prevScript = prev->GetTxOut().scriptPubKey;
if (!Solver(prevScript, IsGenesisEnabled(config, prev.value(), mempoolHeight),
whichType, vSolutions)) {
return false;
}
if (whichType == TX_SCRIPTHASH) {
// Pre-genesis limitations are stricter than post-genesis, so LimitedStack can use UINT32_MAX as max size.
LimitedStack stack(UINT32_MAX);
// convert the scriptSig into a stack, so we can inspect the
// redeemScript
auto res =
EvalScript(
config,
false,
token,
stack,
tx.vin[i].scriptSig,
SCRIPT_VERIFY_NONE,
BaseSignatureChecker());
if (!res.has_value())
{
return {};
}
else if (!res.value())
{
return false;
}
if (stack.empty()) {
return false;
}
// isGenesisEnabled is set to false, because TX_SCRIPTHASH is not supported after genesis
bool sigOpCountError;
CScript subscript(stack.back().begin(), stack.back().end());
uint64_t nSigOpCount = subscript.GetSigOpCount(true, false, sigOpCountError);
if (sigOpCountError || nSigOpCount > MAX_P2SH_SIGOPS) {
return false;
}
}
}
return true;
}