Skip to content

Commit a49381d

Browse files
committed
Merge #12885: Reduce implementation code inside CScript
54a5a21 [MOVEONLY] Turn CScript::GetOp2 into a function and move to cpp (Pieter Wuille) 6a7456a [MOVEONLY] Move CSCript::FindAndDelete to interpreter (Pieter Wuille) 33a8ecf Delete unused non-const-iterator CSCript::GetOp overloads (Pieter Wuille) 2fb168b Make iterators in CScript::FindAndDelete const (Pieter Wuille) Pull request description: This PR moves `FindAndDelete` and `GetOp2` out of CScript (the first is only used inside the interpreter and moved there, the second does not actually depend on any script specifics and works on any vector). Furthermore, all non-const-iterator versions of GetOp are replaced by const ones, removing a number of methods in the process. The longer term goal here is making the script interpreter independent from the CScript representation. Note for reviewers: both `FindAndDelete` and `GetScriptOp` are consensus critical. Tree-SHA512: c4ccf91c0b33c37cff0d474aa8dd2dab25b5b7655e2ed69a9b15e29daf0a67b21d51c23e1defb3a72ec762bd6138de96f69c6db1fb9c1fe1e976e421261aedb7
2 parents 8609ddb + 54a5a21 commit a49381d

File tree

7 files changed

+106
-118
lines changed

7 files changed

+106
-118
lines changed

src/core_write.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ std::string FormatScript(const CScript& script)
3434
while (it != script.end()) {
3535
CScript::const_iterator it2 = it;
3636
std::vector<unsigned char> vch;
37-
if (script.GetOp2(it, op, &vch)) {
37+
if (script.GetOp(it, op, vch)) {
3838
if (op == OP_0) {
3939
ret += "0 ";
4040
continue;

src/script/interpreter.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,34 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
250250
return true;
251251
}
252252

253+
int FindAndDelete(CScript& script, const CScript& b)
254+
{
255+
int nFound = 0;
256+
if (b.empty())
257+
return nFound;
258+
CScript result;
259+
CScript::const_iterator pc = script.begin(), pc2 = script.begin(), end = script.end();
260+
opcodetype opcode;
261+
do
262+
{
263+
result.insert(result.end(), pc2, pc);
264+
while (static_cast<size_t>(end - pc) >= b.size() && std::equal(b.begin(), b.end(), pc))
265+
{
266+
pc = pc + b.size();
267+
++nFound;
268+
}
269+
pc2 = pc;
270+
}
271+
while (script.GetOp(pc, opcode));
272+
273+
if (nFound > 0) {
274+
result.insert(result.end(), pc2, end);
275+
script = std::move(result);
276+
}
277+
278+
return nFound;
279+
}
280+
253281
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
254282
{
255283
static const CScriptNum bnZero(0);
@@ -891,7 +919,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
891919

892920
// Drop the signature in pre-segwit scripts but not segwit scripts
893921
if (sigversion == SigVersion::BASE) {
894-
scriptCode.FindAndDelete(CScript(vchSig));
922+
FindAndDelete(scriptCode, CScript(vchSig));
895923
}
896924

897925
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
@@ -955,7 +983,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
955983
{
956984
valtype& vchSig = stacktop(-isig-k);
957985
if (sigversion == SigVersion::BASE) {
958-
scriptCode.FindAndDelete(CScript(vchSig));
986+
FindAndDelete(scriptCode, CScript(vchSig));
959987
}
960988
}
961989

src/script/interpreter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,6 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
189189

190190
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
191191

192+
int FindAndDelete(CScript& script, const CScript& b);
193+
192194
#endif // BITCOIN_SCRIPT_INTERPRETER_H

src/script/script.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,3 +280,55 @@ bool CScript::HasValidOps() const
280280
}
281281
return true;
282282
}
283+
284+
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet)
285+
{
286+
opcodeRet = OP_INVALIDOPCODE;
287+
if (pvchRet)
288+
pvchRet->clear();
289+
if (pc >= end)
290+
return false;
291+
292+
// Read instruction
293+
if (end - pc < 1)
294+
return false;
295+
unsigned int opcode = *pc++;
296+
297+
// Immediate operand
298+
if (opcode <= OP_PUSHDATA4)
299+
{
300+
unsigned int nSize = 0;
301+
if (opcode < OP_PUSHDATA1)
302+
{
303+
nSize = opcode;
304+
}
305+
else if (opcode == OP_PUSHDATA1)
306+
{
307+
if (end - pc < 1)
308+
return false;
309+
nSize = *pc++;
310+
}
311+
else if (opcode == OP_PUSHDATA2)
312+
{
313+
if (end - pc < 2)
314+
return false;
315+
nSize = ReadLE16(&pc[0]);
316+
pc += 2;
317+
}
318+
else if (opcode == OP_PUSHDATA4)
319+
{
320+
if (end - pc < 4)
321+
return false;
322+
nSize = ReadLE32(&pc[0]);
323+
pc += 4;
324+
}
325+
if (end - pc < 0 || (unsigned int)(end - pc) < nSize)
326+
return false;
327+
if (pvchRet)
328+
pvchRet->assign(pc, pc + nSize);
329+
pc += nSize;
330+
}
331+
332+
opcodeRet = static_cast<opcodetype>(opcode);
333+
return true;
334+
}

src/script/script.h

Lines changed: 4 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,8 @@ class CScriptNum
385385
*/
386386
typedef prevector<28, unsigned char> CScriptBase;
387387

388+
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet);
389+
388390
/** Serialized script, used inside transaction inputs and outputs */
389391
class CScript : public CScriptBase
390392
{
@@ -493,84 +495,16 @@ class CScript : public CScriptBase
493495
}
494496

495497

496-
bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet)
497-
{
498-
// Wrapper so it can be called with either iterator or const_iterator
499-
const_iterator pc2 = pc;
500-
bool fRet = GetOp2(pc2, opcodeRet, &vchRet);
501-
pc = begin() + (pc2 - begin());
502-
return fRet;
503-
}
504-
505-
bool GetOp(iterator& pc, opcodetype& opcodeRet)
506-
{
507-
const_iterator pc2 = pc;
508-
bool fRet = GetOp2(pc2, opcodeRet, nullptr);
509-
pc = begin() + (pc2 - begin());
510-
return fRet;
511-
}
512-
513498
bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) const
514499
{
515-
return GetOp2(pc, opcodeRet, &vchRet);
500+
return GetScriptOp(pc, end(), opcodeRet, &vchRet);
516501
}
517502

518503
bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const
519504
{
520-
return GetOp2(pc, opcodeRet, nullptr);
505+
return GetScriptOp(pc, end(), opcodeRet, nullptr);
521506
}
522507

523-
bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) const
524-
{
525-
opcodeRet = OP_INVALIDOPCODE;
526-
if (pvchRet)
527-
pvchRet->clear();
528-
if (pc >= end())
529-
return false;
530-
531-
// Read instruction
532-
if (end() - pc < 1)
533-
return false;
534-
unsigned int opcode = *pc++;
535-
536-
// Immediate operand
537-
if (opcode <= OP_PUSHDATA4)
538-
{
539-
unsigned int nSize = 0;
540-
if (opcode < OP_PUSHDATA1)
541-
{
542-
nSize = opcode;
543-
}
544-
else if (opcode == OP_PUSHDATA1)
545-
{
546-
if (end() - pc < 1)
547-
return false;
548-
nSize = *pc++;
549-
}
550-
else if (opcode == OP_PUSHDATA2)
551-
{
552-
if (end() - pc < 2)
553-
return false;
554-
nSize = ReadLE16(&pc[0]);
555-
pc += 2;
556-
}
557-
else if (opcode == OP_PUSHDATA4)
558-
{
559-
if (end() - pc < 4)
560-
return false;
561-
nSize = ReadLE32(&pc[0]);
562-
pc += 4;
563-
}
564-
if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize)
565-
return false;
566-
if (pvchRet)
567-
pvchRet->assign(pc, pc + nSize);
568-
pc += nSize;
569-
}
570-
571-
opcodeRet = static_cast<opcodetype>(opcode);
572-
return true;
573-
}
574508

575509
/** Encode/decode small integers: */
576510
static int DecodeOP_N(opcodetype opcode)
@@ -588,34 +522,6 @@ class CScript : public CScriptBase
588522
return (opcodetype)(OP_1+n-1);
589523
}
590524

591-
int FindAndDelete(const CScript& b)
592-
{
593-
int nFound = 0;
594-
if (b.empty())
595-
return nFound;
596-
CScript result;
597-
iterator pc = begin(), pc2 = begin();
598-
opcodetype opcode;
599-
do
600-
{
601-
result.insert(result.end(), pc2, pc);
602-
while (static_cast<size_t>(end() - pc) >= b.size() && std::equal(b.begin(), b.end(), pc))
603-
{
604-
pc = pc + b.size();
605-
++nFound;
606-
}
607-
pc2 = pc;
608-
}
609-
while (GetOp(pc, opcode));
610-
611-
if (nFound > 0) {
612-
result.insert(result.end(), pc2, end());
613-
*this = result;
614-
}
615-
616-
return nFound;
617-
}
618-
619525
/**
620526
* Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
621527
* as 20 sigops. With pay-to-script-hash, that changed:

src/test/script_tests.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,102 +1349,102 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete)
13491349
s = CScript() << OP_1 << OP_2;
13501350
d = CScript(); // delete nothing should be a no-op
13511351
expect = s;
1352-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
1352+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
13531353
BOOST_CHECK(s == expect);
13541354

13551355
s = CScript() << OP_1 << OP_2 << OP_3;
13561356
d = CScript() << OP_2;
13571357
expect = CScript() << OP_1 << OP_3;
1358-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1358+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
13591359
BOOST_CHECK(s == expect);
13601360

13611361
s = CScript() << OP_3 << OP_1 << OP_3 << OP_3 << OP_4 << OP_3;
13621362
d = CScript() << OP_3;
13631363
expect = CScript() << OP_1 << OP_4;
1364-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 4);
1364+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 4);
13651365
BOOST_CHECK(s == expect);
13661366

13671367
s = ScriptFromHex("0302ff03"); // PUSH 0x02ff03 onto stack
13681368
d = ScriptFromHex("0302ff03");
13691369
expect = CScript();
1370-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1370+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
13711371
BOOST_CHECK(s == expect);
13721372

13731373
s = ScriptFromHex("0302ff030302ff03"); // PUSH 0x2ff03 PUSH 0x2ff03
13741374
d = ScriptFromHex("0302ff03");
13751375
expect = CScript();
1376-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
1376+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2);
13771377
BOOST_CHECK(s == expect);
13781378

13791379
s = ScriptFromHex("0302ff030302ff03");
13801380
d = ScriptFromHex("02");
13811381
expect = s; // FindAndDelete matches entire opcodes
1382-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
1382+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
13831383
BOOST_CHECK(s == expect);
13841384

13851385
s = ScriptFromHex("0302ff030302ff03");
13861386
d = ScriptFromHex("ff");
13871387
expect = s;
1388-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
1388+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
13891389
BOOST_CHECK(s == expect);
13901390

13911391
// This is an odd edge case: strip of the push-three-bytes
13921392
// prefix, leaving 02ff03 which is push-two-bytes:
13931393
s = ScriptFromHex("0302ff030302ff03");
13941394
d = ScriptFromHex("03");
13951395
expect = CScript() << ParseHex("ff03") << ParseHex("ff03");
1396-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
1396+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2);
13971397
BOOST_CHECK(s == expect);
13981398

13991399
// Byte sequence that spans multiple opcodes:
14001400
s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY
14011401
d = ScriptFromHex("feed51");
14021402
expect = s;
1403-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); // doesn't match 'inside' opcodes
1403+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); // doesn't match 'inside' opcodes
14041404
BOOST_CHECK(s == expect);
14051405

14061406
s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY
14071407
d = ScriptFromHex("02feed51");
14081408
expect = ScriptFromHex("69");
1409-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1409+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
14101410
BOOST_CHECK(s == expect);
14111411

14121412
s = ScriptFromHex("516902feed5169");
14131413
d = ScriptFromHex("feed51");
14141414
expect = s;
1415-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
1415+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
14161416
BOOST_CHECK(s == expect);
14171417

14181418
s = ScriptFromHex("516902feed5169");
14191419
d = ScriptFromHex("02feed51");
14201420
expect = ScriptFromHex("516969");
1421-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1421+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
14221422
BOOST_CHECK(s == expect);
14231423

14241424
s = CScript() << OP_0 << OP_0 << OP_1 << OP_1;
14251425
d = CScript() << OP_0 << OP_1;
14261426
expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass
1427-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1427+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
14281428
BOOST_CHECK(s == expect);
14291429

14301430
s = CScript() << OP_0 << OP_0 << OP_1 << OP_0 << OP_1 << OP_1;
14311431
d = CScript() << OP_0 << OP_1;
14321432
expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass
1433-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
1433+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2);
14341434
BOOST_CHECK(s == expect);
14351435

14361436
// Another weird edge case:
14371437
// End with invalid push (not enough data)...
14381438
s = ScriptFromHex("0003feed");
14391439
d = ScriptFromHex("03feed"); // ... can remove the invalid push
14401440
expect = ScriptFromHex("00");
1441-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1441+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
14421442
BOOST_CHECK(s == expect);
14431443

14441444
s = ScriptFromHex("0003feed");
14451445
d = ScriptFromHex("00");
14461446
expect = ScriptFromHex("03feed");
1447-
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
1447+
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
14481448
BOOST_CHECK(s == expect);
14491449
}
14501450

src/test/sighash_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
3535

3636
// In case concatenating two scripts ends up with two codeseparators,
3737
// or an extra one at the end, this prevents all those possible incompatibilities.
38-
scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));
38+
FindAndDelete(scriptCode, CScript(OP_CODESEPARATOR));
3939

4040
// Blank out other inputs' signatures
4141
for (unsigned int i = 0; i < txTmp.vin.size(); i++)

0 commit comments

Comments
 (0)