upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-07-30 18:22:48 -0300
committerGitHub <noreply@github.com>2026-07-30 18:22:48 -0300
commit773440fcc7752b85d99ee5b8500b64dbb5869603 (patch)
treeb0559564577797486bd44e043f2c0691f1610105
parent50eeb9c9d620895c0d63664d610ea4cfe7b81a34 (diff)
script: bound how many operations and stack entries a script may use (#57)
v1.2.1 disabled the unbounded opcodes and capped a single pushed element at 520 bytes. What it did not add was a ceiling on how many items there can be, so a script still pays for itself once, in bytes, and then multiplies: 520 bytes per stack entry means a script of N bytes made almost entirely of OP_DUP occupies roughly 520N in memory, on every node that validates it. Two limits, both from Bitcoin: - at most 201 operations per script, counted outside the fExec test so the cost of a script cannot depend on which branch a signature happens to take. Pushes are exempt, being already bounded by the script length and by MAX_SCRIPT_ELEMENT_SIZE. - at most 1000 entries across both stacks together, counted together because OP_TOALTSTACK would otherwise move the problem rather than solve it. This tightens validation, so a node that does not upgrade accepts a superset of what this one accepts and is not stranded. Verified against the live chain: a node with these rules synced all 3910 blocks from genesis and accepted every one, so nothing in use relies on scripts these limits would reject.
-rw-r--r--src/script.cpp13
-rw-r--r--src/script.h9
2 files changed, 22 insertions, 0 deletions
diff --git a/src/script.cpp b/src/script.cpp
index 44eed93..6d4fb34 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -51,6 +51,7 @@ bool EvalScript(const CScript& script, const CTransaction& txTo, unsigned int nI
51 vector<bool> vfExec; 51 vector<bool> vfExec;
52 vector<valtype> stack; 52 vector<valtype> stack;
53 vector<valtype> altstack; 53 vector<valtype> altstack;
54 unsigned int nOpCount = 0;
54 if (pvStackRet) 55 if (pvStackRet)
55 pvStackRet->clear(); 56 pvStackRet->clear();
56 57
@@ -85,6 +86,13 @@ bool EvalScript(const CScript& script, const CTransaction& txTo, unsigned int nI
85 opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT) 86 opcode == OP_MOD || opcode == OP_LSHIFT || opcode == OP_RSHIFT)
86 return false; 87 return false;
87 88
89 // Counted outside fExec for the same reason: the cost of a script must
90 // not depend on which branch a signature happens to take. Pushes are
91 // exempt because their cost is already bounded by the script's own
92 // length and by MAX_SCRIPT_ELEMENT_SIZE.
93 if (opcode > OP_16 && ++nOpCount > MAX_OPS_PER_SCRIPT)
94 return false;
95
88 if (fExec && opcode <= OP_PUSHDATA4) 96 if (fExec && opcode <= OP_PUSHDATA4)
89 stack.push_back(vchPushValue); 97 stack.push_back(vchPushValue);
90 else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) 98 else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF))
@@ -824,6 +832,11 @@ bool EvalScript(const CScript& script, const CTransaction& txTo, unsigned int nI
824 default: 832 default:
825 return false; 833 return false;
826 } 834 }
835
836 // Both stacks together, because OP_TOALTSTACK would otherwise move the
837 // problem rather than solve it.
838 if (stack.size() + altstack.size() > MAX_STACK_SIZE)
839 return false;
827 } 840 }
828 841
829 842
diff --git a/src/script.h b/src/script.h
index 26bb65a..d36a317 100644
--- a/src/script.h
+++ b/src/script.h
@@ -8,6 +8,15 @@ class CTransaction;
8// script evaluation allocates whatever the transaction asks it to. 8// script evaluation allocates whatever the transaction asks it to.
9static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; 9static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520;
10 10
11// Bounds on what one script may do while being evaluated. The element cap
12// above limits how big a single item is; these limit how many there can be and
13// how much work producing them costs. Without them, a script pays for itself
14// once, in bytes, and then multiplies: 520 bytes per stack entry means a
15// script of N bytes made almost entirely of OP_DUP occupies roughly 520N in
16// memory, and every node that validates it pays.
17static const unsigned int MAX_OPS_PER_SCRIPT = 201;
18static const unsigned int MAX_STACK_SIZE = 1000;
19
11enum 20enum
12{ 21{
13 SIGHASH_ALL = 1, 22 SIGHASH_ALL = 1,