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
This commit is contained in:
commit
a49381dfa3
7 changed files with 106 additions and 118 deletions
|
@ -34,7 +34,7 @@ std::string FormatScript(const CScript& script)
|
|||
while (it != script.end()) {
|
||||
CScript::const_iterator it2 = it;
|
||||
std::vector<unsigned char> vch;
|
||||
if (script.GetOp2(it, op, &vch)) {
|
||||
if (script.GetOp(it, op, vch)) {
|
||||
if (op == OP_0) {
|
||||
ret += "0 ";
|
||||
continue;
|
||||
|
|
|
@ -250,6 +250,34 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
|
|||
return true;
|
||||
}
|
||||
|
||||
int FindAndDelete(CScript& script, const CScript& b)
|
||||
{
|
||||
int nFound = 0;
|
||||
if (b.empty())
|
||||
return nFound;
|
||||
CScript result;
|
||||
CScript::const_iterator pc = script.begin(), pc2 = script.begin(), end = script.end();
|
||||
opcodetype opcode;
|
||||
do
|
||||
{
|
||||
result.insert(result.end(), pc2, pc);
|
||||
while (static_cast<size_t>(end - pc) >= b.size() && std::equal(b.begin(), b.end(), pc))
|
||||
{
|
||||
pc = pc + b.size();
|
||||
++nFound;
|
||||
}
|
||||
pc2 = pc;
|
||||
}
|
||||
while (script.GetOp(pc, opcode));
|
||||
|
||||
if (nFound > 0) {
|
||||
result.insert(result.end(), pc2, end);
|
||||
script = std::move(result);
|
||||
}
|
||||
|
||||
return nFound;
|
||||
}
|
||||
|
||||
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* serror)
|
||||
{
|
||||
static const CScriptNum bnZero(0);
|
||||
|
@ -891,7 +919,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
|
|||
|
||||
// Drop the signature in pre-segwit scripts but not segwit scripts
|
||||
if (sigversion == SigVersion::BASE) {
|
||||
scriptCode.FindAndDelete(CScript(vchSig));
|
||||
FindAndDelete(scriptCode, CScript(vchSig));
|
||||
}
|
||||
|
||||
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&
|
|||
{
|
||||
valtype& vchSig = stacktop(-isig-k);
|
||||
if (sigversion == SigVersion::BASE) {
|
||||
scriptCode.FindAndDelete(CScript(vchSig));
|
||||
FindAndDelete(scriptCode, CScript(vchSig));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -189,4 +189,6 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
|
|||
|
||||
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
|
||||
|
||||
int FindAndDelete(CScript& script, const CScript& b);
|
||||
|
||||
#endif // BITCOIN_SCRIPT_INTERPRETER_H
|
||||
|
|
|
@ -280,3 +280,55 @@ bool CScript::HasValidOps() const
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet)
|
||||
{
|
||||
opcodeRet = OP_INVALIDOPCODE;
|
||||
if (pvchRet)
|
||||
pvchRet->clear();
|
||||
if (pc >= end)
|
||||
return false;
|
||||
|
||||
// Read instruction
|
||||
if (end - pc < 1)
|
||||
return false;
|
||||
unsigned int opcode = *pc++;
|
||||
|
||||
// Immediate operand
|
||||
if (opcode <= OP_PUSHDATA4)
|
||||
{
|
||||
unsigned int nSize = 0;
|
||||
if (opcode < OP_PUSHDATA1)
|
||||
{
|
||||
nSize = opcode;
|
||||
}
|
||||
else if (opcode == OP_PUSHDATA1)
|
||||
{
|
||||
if (end - pc < 1)
|
||||
return false;
|
||||
nSize = *pc++;
|
||||
}
|
||||
else if (opcode == OP_PUSHDATA2)
|
||||
{
|
||||
if (end - pc < 2)
|
||||
return false;
|
||||
nSize = ReadLE16(&pc[0]);
|
||||
pc += 2;
|
||||
}
|
||||
else if (opcode == OP_PUSHDATA4)
|
||||
{
|
||||
if (end - pc < 4)
|
||||
return false;
|
||||
nSize = ReadLE32(&pc[0]);
|
||||
pc += 4;
|
||||
}
|
||||
if (end - pc < 0 || (unsigned int)(end - pc) < nSize)
|
||||
return false;
|
||||
if (pvchRet)
|
||||
pvchRet->assign(pc, pc + nSize);
|
||||
pc += nSize;
|
||||
}
|
||||
|
||||
opcodeRet = static_cast<opcodetype>(opcode);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -385,6 +385,8 @@ private:
|
|||
*/
|
||||
typedef prevector<28, unsigned char> CScriptBase;
|
||||
|
||||
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet);
|
||||
|
||||
/** Serialized script, used inside transaction inputs and outputs */
|
||||
class CScript : public CScriptBase
|
||||
{
|
||||
|
@ -493,84 +495,16 @@ public:
|
|||
}
|
||||
|
||||
|
||||
bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet)
|
||||
{
|
||||
// Wrapper so it can be called with either iterator or const_iterator
|
||||
const_iterator pc2 = pc;
|
||||
bool fRet = GetOp2(pc2, opcodeRet, &vchRet);
|
||||
pc = begin() + (pc2 - begin());
|
||||
return fRet;
|
||||
}
|
||||
|
||||
bool GetOp(iterator& pc, opcodetype& opcodeRet)
|
||||
{
|
||||
const_iterator pc2 = pc;
|
||||
bool fRet = GetOp2(pc2, opcodeRet, nullptr);
|
||||
pc = begin() + (pc2 - begin());
|
||||
return fRet;
|
||||
}
|
||||
|
||||
bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) const
|
||||
{
|
||||
return GetOp2(pc, opcodeRet, &vchRet);
|
||||
return GetScriptOp(pc, end(), opcodeRet, &vchRet);
|
||||
}
|
||||
|
||||
bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const
|
||||
{
|
||||
return GetOp2(pc, opcodeRet, nullptr);
|
||||
return GetScriptOp(pc, end(), opcodeRet, nullptr);
|
||||
}
|
||||
|
||||
bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) const
|
||||
{
|
||||
opcodeRet = OP_INVALIDOPCODE;
|
||||
if (pvchRet)
|
||||
pvchRet->clear();
|
||||
if (pc >= end())
|
||||
return false;
|
||||
|
||||
// Read instruction
|
||||
if (end() - pc < 1)
|
||||
return false;
|
||||
unsigned int opcode = *pc++;
|
||||
|
||||
// Immediate operand
|
||||
if (opcode <= OP_PUSHDATA4)
|
||||
{
|
||||
unsigned int nSize = 0;
|
||||
if (opcode < OP_PUSHDATA1)
|
||||
{
|
||||
nSize = opcode;
|
||||
}
|
||||
else if (opcode == OP_PUSHDATA1)
|
||||
{
|
||||
if (end() - pc < 1)
|
||||
return false;
|
||||
nSize = *pc++;
|
||||
}
|
||||
else if (opcode == OP_PUSHDATA2)
|
||||
{
|
||||
if (end() - pc < 2)
|
||||
return false;
|
||||
nSize = ReadLE16(&pc[0]);
|
||||
pc += 2;
|
||||
}
|
||||
else if (opcode == OP_PUSHDATA4)
|
||||
{
|
||||
if (end() - pc < 4)
|
||||
return false;
|
||||
nSize = ReadLE32(&pc[0]);
|
||||
pc += 4;
|
||||
}
|
||||
if (end() - pc < 0 || (unsigned int)(end() - pc) < nSize)
|
||||
return false;
|
||||
if (pvchRet)
|
||||
pvchRet->assign(pc, pc + nSize);
|
||||
pc += nSize;
|
||||
}
|
||||
|
||||
opcodeRet = static_cast<opcodetype>(opcode);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Encode/decode small integers: */
|
||||
static int DecodeOP_N(opcodetype opcode)
|
||||
|
@ -588,34 +522,6 @@ public:
|
|||
return (opcodetype)(OP_1+n-1);
|
||||
}
|
||||
|
||||
int FindAndDelete(const CScript& b)
|
||||
{
|
||||
int nFound = 0;
|
||||
if (b.empty())
|
||||
return nFound;
|
||||
CScript result;
|
||||
iterator pc = begin(), pc2 = begin();
|
||||
opcodetype opcode;
|
||||
do
|
||||
{
|
||||
result.insert(result.end(), pc2, pc);
|
||||
while (static_cast<size_t>(end() - pc) >= b.size() && std::equal(b.begin(), b.end(), pc))
|
||||
{
|
||||
pc = pc + b.size();
|
||||
++nFound;
|
||||
}
|
||||
pc2 = pc;
|
||||
}
|
||||
while (GetOp(pc, opcode));
|
||||
|
||||
if (nFound > 0) {
|
||||
result.insert(result.end(), pc2, end());
|
||||
*this = result;
|
||||
}
|
||||
|
||||
return nFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-version-0.6, Bitcoin always counted CHECKMULTISIGs
|
||||
* as 20 sigops. With pay-to-script-hash, that changed:
|
||||
|
|
|
@ -1349,43 +1349,43 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete)
|
|||
s = CScript() << OP_1 << OP_2;
|
||||
d = CScript(); // delete nothing should be a no-op
|
||||
expect = s;
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = CScript() << OP_1 << OP_2 << OP_3;
|
||||
d = CScript() << OP_2;
|
||||
expect = CScript() << OP_1 << OP_3;
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = CScript() << OP_3 << OP_1 << OP_3 << OP_3 << OP_4 << OP_3;
|
||||
d = CScript() << OP_3;
|
||||
expect = CScript() << OP_1 << OP_4;
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 4);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 4);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = ScriptFromHex("0302ff03"); // PUSH 0x02ff03 onto stack
|
||||
d = ScriptFromHex("0302ff03");
|
||||
expect = CScript();
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = ScriptFromHex("0302ff030302ff03"); // PUSH 0x2ff03 PUSH 0x2ff03
|
||||
d = ScriptFromHex("0302ff03");
|
||||
expect = CScript();
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = ScriptFromHex("0302ff030302ff03");
|
||||
d = ScriptFromHex("02");
|
||||
expect = s; // FindAndDelete matches entire opcodes
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = ScriptFromHex("0302ff030302ff03");
|
||||
d = ScriptFromHex("ff");
|
||||
expect = s;
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
// This is an odd edge case: strip of the push-three-bytes
|
||||
|
@ -1393,44 +1393,44 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete)
|
|||
s = ScriptFromHex("0302ff030302ff03");
|
||||
d = ScriptFromHex("03");
|
||||
expect = CScript() << ParseHex("ff03") << ParseHex("ff03");
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
// Byte sequence that spans multiple opcodes:
|
||||
s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY
|
||||
d = ScriptFromHex("feed51");
|
||||
expect = s;
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0); // doesn't match 'inside' opcodes
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0); // doesn't match 'inside' opcodes
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = ScriptFromHex("02feed5169"); // PUSH(0xfeed) OP_1 OP_VERIFY
|
||||
d = ScriptFromHex("02feed51");
|
||||
expect = ScriptFromHex("69");
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = ScriptFromHex("516902feed5169");
|
||||
d = ScriptFromHex("feed51");
|
||||
expect = s;
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 0);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 0);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = ScriptFromHex("516902feed5169");
|
||||
d = ScriptFromHex("02feed51");
|
||||
expect = ScriptFromHex("516969");
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = CScript() << OP_0 << OP_0 << OP_1 << OP_1;
|
||||
d = CScript() << OP_0 << OP_1;
|
||||
expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = CScript() << OP_0 << OP_0 << OP_1 << OP_0 << OP_1 << OP_1;
|
||||
d = CScript() << OP_0 << OP_1;
|
||||
expect = CScript() << OP_0 << OP_1; // FindAndDelete is single-pass
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 2);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 2);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
// Another weird edge case:
|
||||
|
@ -1438,13 +1438,13 @@ BOOST_AUTO_TEST_CASE(script_FindAndDelete)
|
|||
s = ScriptFromHex("0003feed");
|
||||
d = ScriptFromHex("03feed"); // ... can remove the invalid push
|
||||
expect = ScriptFromHex("00");
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
|
||||
BOOST_CHECK(s == expect);
|
||||
|
||||
s = ScriptFromHex("0003feed");
|
||||
d = ScriptFromHex("00");
|
||||
expect = ScriptFromHex("03feed");
|
||||
BOOST_CHECK_EQUAL(s.FindAndDelete(d), 1);
|
||||
BOOST_CHECK_EQUAL(FindAndDelete(s, d), 1);
|
||||
BOOST_CHECK(s == expect);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, un
|
|||
|
||||
// In case concatenating two scripts ends up with two codeseparators,
|
||||
// or an extra one at the end, this prevents all those possible incompatibilities.
|
||||
scriptCode.FindAndDelete(CScript(OP_CODESEPARATOR));
|
||||
FindAndDelete(scriptCode, CScript(OP_CODESEPARATOR));
|
||||
|
||||
// Blank out other inputs' signatures
|
||||
for (unsigned int i = 0; i < txTmp.vin.size(); i++)
|
||||
|
|
Loading…
Reference in a new issue