View
@@ -35,10 +35,18 @@ static const std::map<std::string, CScriptFlag> mapFlagNames = {
{std::string("WITNESS_PUBKEYTYPE"), SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
};
-CScriptFlag ParseScriptFlag(const std::string flag_name)
+unsigned int ParseScriptFlag(const std::string flag_name)
{
const auto it = mapFlagNames.find(flag_name);
if (it == mapFlagNames.end()) {
+ if (flag_name == "ALL") {
+ unsigned int ret = 0;
+ auto it = mapFlagNames.begin();
+ while (it != mapFlagNames.end()) {
+ ret |= it->second;
+ }
+ return ret;
+ }
throw std::runtime_error(std::string(__func__) + ": unknown verification flag '" + flag_name + "'");
}
return it->second;
@@ -60,6 +68,26 @@ std::vector<std::string> ScriptFlagsToStrings(unsigned int flags)
return ret;
}
+std::string SigVersionString(const SigVersion sigver)
+{
+ switch (sigver) {
+ case SIGVERSION_BASE: return "Base";
+ case SIGVERSION_WITNESS_V0: return "Witness_V0";
+ }
+ return "(unknown)";
+}
+
+std::string ScriptExecution::ContextString(const Context ctx)
+{
+ switch (ctx) {
+ case ScriptExecution::Context::Sig: return "Sig";
+ case ScriptExecution::Context::PubKey: return "PubKey";
+ case ScriptExecution::Context::BIP16: return "BIP16";
+ case ScriptExecution::Context::Segwit: return "Segwit";
+ }
+ return "(unknown)";
+}
+
namespace {
inline bool set_success(ScriptError* ret)
@@ -288,8 +316,9 @@ bool static CheckMinimalPush(const valtype& data, opcodetype opcode) {
return true;
}
-ScriptExecution::ScriptExecution(StackType& stack_in, const CScript& script_in, unsigned int flags_in, const BaseSignatureChecker& checker_in, SigVersion sigversion_in) :
- script(script_in), stack(stack_in), flags(flags_in), checker(checker_in), sigversion(sigversion_in), pc(script.begin()), pbegincodehash(script.begin()), nOpCount(0)
+ScriptExecution::ScriptExecution(ScriptExecution::Context context_in, StackType& stack_in, const CScript& script_in, unsigned int flags_in, const BaseSignatureChecker& checker_in, SigVersion sigversion_in) :
+ context(context_in), script(script_in), stack(stack_in), flags(flags_in), checker(checker_in), sigversion(sigversion_in), pc(script.begin()), pbegincodehash(script.begin()), nOpCount(0),
+ debugger(nullptr)
{
}
@@ -303,9 +332,15 @@ bool ScriptExecution::Eval(ScriptError* serror)
// static const valtype vchZero(0);
static const valtype vchTrue(1, 1);
+ CScript::const_iterator pcur = pc;
const CScript::const_iterator pend = script.end();
opcodetype opcode;
valtype vchPushValue;
+
+ if (debugger) {
+ debugger->ScriptBegin(*this);
+ }
+
set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
if (script.size() > MAX_SCRIPT_SIZE)
return set_error(serror, SCRIPT_ERR_SCRIPT_SIZE);
@@ -320,8 +355,14 @@ bool ScriptExecution::Eval(ScriptError* serror)
//
// Read instruction
//
+ pcur = pc;
if (!script.GetOp(pc, opcode, vchPushValue))
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
+
+ if (debugger) {
+ debugger->ScriptPreStep(*this, pcur, opcode, vchPushValue);
+ }
+
if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE)
return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
@@ -1082,6 +1123,10 @@ bool ScriptExecution::Eval(ScriptError* serror)
return set_error(serror, SCRIPT_ERR_UNKNOWN_ERROR);
}
+ if (debugger) {
+ debugger->ScriptEOF(*this, pc);
+ }
+
if (!vfExec.empty())
return set_error(serror, SCRIPT_ERR_UNBALANCED_CONDITIONAL);
@@ -1396,7 +1441,7 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con
return true;
}
-static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
+static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, ScriptExecutionDebugger * const debugger)
{
std::vector<std::vector<unsigned char> > stack;
CScript scriptPubKey;
@@ -1437,7 +1482,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
return set_error(serror, SCRIPT_ERR_PUSH_SIZE);
}
- if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_WITNESS_V0, serror)) {
+ if (!EvalScript(ScriptExecution::Context::Segwit, stack, scriptPubKey, flags, checker, SIGVERSION_WITNESS_V0, serror, debugger)) {
return false;
}
@@ -1449,7 +1494,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion,
return true;
}
-bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror)
+bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror, ScriptExecutionDebugger * const debugger)
{
static const CScriptWitness emptyWitness;
if (witness == nullptr) {
@@ -1464,12 +1509,12 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
}
std::vector<std::vector<unsigned char> > stack, stackCopy;
- if (!EvalScript(stack, scriptSig, flags, checker, SIGVERSION_BASE, serror))
+ if (!EvalScript(ScriptExecution::Context::Sig, stack, scriptSig, flags, checker, SIGVERSION_BASE, serror, debugger))
// serror is set
return false;
if (flags & SCRIPT_VERIFY_P2SH)
stackCopy = stack;
- if (!EvalScript(stack, scriptPubKey, flags, checker, SIGVERSION_BASE, serror))
+ if (!EvalScript(ScriptExecution::Context::PubKey, stack, scriptPubKey, flags, checker, SIGVERSION_BASE, serror, debugger))
// serror is set
return false;
if (stack.empty())
@@ -1487,7 +1532,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
// The scriptSig must be _exactly_ CScript(), otherwise we reintroduce malleability.
return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED);
}
- if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) {
+ if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, debugger)) {
return false;
}
// Bypass the cleanstack check at the end. The actual stack is obviously not clean
@@ -1515,7 +1560,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
popstack(stack);
- if (!EvalScript(stack, pubKey2, flags, checker, SIGVERSION_BASE, serror))
+ if (!EvalScript(ScriptExecution::Context::BIP16, stack, pubKey2, flags, checker, SIGVERSION_BASE, serror, debugger))
// serror is set
return false;
if (stack.empty())
@@ -1532,7 +1577,7 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
// reintroduce malleability.
return set_error(serror, SCRIPT_ERR_WITNESS_MALLEATED_P2SH);
}
- if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror)) {
+ if (!VerifyWitnessProgram(*witness, witnessversion, witnessprogram, flags, checker, serror, debugger)) {
return false;
}
// Bypass the cleanstack check at the end. The actual stack is obviously not clean
View
@@ -108,7 +108,7 @@ enum CScriptFlag
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15),
};
-CScriptFlag ParseScriptFlag(const std::string flag_name);
+unsigned int ParseScriptFlag(const std::string flag_name);
std::vector<std::string> ScriptFlagsToStrings(unsigned int flags);
bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror);
@@ -126,6 +126,8 @@ enum SigVersion
SIGVERSION_WITNESS_V0 = 1,
};
+std::string SigVersionString(SigVersion);
+
uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr);
class BaseSignatureChecker
@@ -177,11 +179,22 @@ class MutableTransactionSignatureChecker : public TransactionSignatureChecker
MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn), txTo(*txToIn) {}
};
+class ScriptExecutionDebugger;
+
class ScriptExecution {
public:
typedef std::vector<unsigned char> StackElementType;
typedef std::vector<StackElementType> StackType;
+ enum class Context {
+ Sig,
+ PubKey,
+ BIP16,
+ Segwit,
+ };
+
+ static std::string ContextString(Context);
+ Context context;
const CScript& script;
StackType& stack;
unsigned int flags;
@@ -195,17 +208,33 @@ class ScriptExecution {
StackType altstack;
int nOpCount;
- ScriptExecution(StackType& stack, const CScript&, unsigned int flags, const BaseSignatureChecker&, SigVersion);
+ ScriptExecutionDebugger *debugger;
+
+ ScriptExecution(Context, StackType& stack, const CScript&, unsigned int flags, const BaseSignatureChecker&, SigVersion);
bool Eval(ScriptError* error = nullptr);
};
-inline bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr)
+class ScriptExecutionDebugger {
+public:
+ void *userdata;
+
+protected:
+ virtual void ScriptBegin(ScriptExecution&) {}
+ virtual void ScriptPreStep(ScriptExecution&, const CScript::const_iterator&, opcodetype&, ScriptExecution::StackElementType&) {}
+ virtual void ScriptEOF(ScriptExecution&, const CScript::const_iterator&) {}
+
+ friend bool ScriptExecution::Eval(ScriptError* serror);
+};
+
+inline bool EvalScript(ScriptExecution::Context context, std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr, ScriptExecutionDebugger * const debugger = nullptr)
{
- return ScriptExecution(stack, script, flags, checker, sigversion).Eval(error);
+ ScriptExecution executor(context, stack, script, flags, checker, sigversion);
+ executor.debugger = debugger;
+ return executor.Eval(error);
}
-bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr);
+bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr, ScriptExecutionDebugger *debugger = nullptr);
size_t CountWitnessSigOps(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags);
View
@@ -289,7 +289,7 @@ struct Stacks
Stacks() {}
explicit Stacks(const std::vector<valtype>& scriptSigStack_) : script(scriptSigStack_), witness() {}
explicit Stacks(const SignatureData& data) : witness(data.scriptWitness.stack) {
- EvalScript(script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE);
+ EvalScript(ScriptExecution::Context::Sig, script, data.scriptSig, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE);
}
SignatureData Output() const {
View
@@ -153,6 +153,45 @@ CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CSc
return txSpend;
}
+extern "C" {
+ static void script_tests_debugger_ScriptBegin(void *userdata, struct bitcoinconsensus_script_execution*);
+ static void script_tests_debugger_ScriptPreStep(void *userdata, struct bitcoinconsensus_script_execution*, size_t pos, const uint8_t* opcode, const void* pushdata, size_t* pushdata_sz);
+ static void script_tests_debugger_ScriptEOF(void *userdata, struct bitcoinconsensus_script_execution*, size_t pos);
+}
+
+class script_tests_debugger {
+public:
+ size_t begin;
+ size_t prestep;
+ size_t eof;
+
+ script_tests_debugger() : begin(0), prestep(0), eof(0) {}
+};
+
+static void script_tests_debugger_ScriptBegin(void *userdata, struct bitcoinconsensus_script_execution*)
+{
+ auto data = (script_tests_debugger*)userdata;
+ ++data->begin;
+}
+
+static void script_tests_debugger_ScriptPreStep(void *userdata, struct bitcoinconsensus_script_execution*, size_t pos, const uint8_t* opcode, const void* pushdata, size_t* pushdata_sz)
+{
+ auto data = (script_tests_debugger*)userdata;
+ ++data->prestep;
+}
+
+static void script_tests_debugger_ScriptEOF(void *userdata, struct bitcoinconsensus_script_execution*, size_t pos)
+{
+ auto data = (script_tests_debugger*)userdata;
+ ++data->eof;
+}
+
+static const struct bitcoinconsensus_script_debugger_callbacks debugger_cbs = {
+ .ScriptBegin = script_tests_debugger_ScriptBegin,
+ .ScriptPreStep = script_tests_debugger_ScriptPreStep,
+ .ScriptEOF = script_tests_debugger_ScriptEOF,
+};
+
void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& scriptWitness, int flags, const std::string& message, int scriptError, CAmount nValue = 0)
{
bool expect = (scriptError == SCRIPT_ERR_OK);
@@ -177,6 +216,13 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript
BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script_with_amount(scriptPubKey.data(), scriptPubKey.size(), 0, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, nullptr) == expect, message);
BOOST_CHECK_MESSAGE(bitcoinconsensus_verify_script(scriptPubKey.data(), scriptPubKey.size(), (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, nullptr) == expect,message);
}
+
+ // Test debugging capabilities
+ script_tests_debugger dbgdata;
+ BOOST_CHECK_MESSAGE(bitcoinconsensus_trace_script(scriptPubKey.data(), scriptPubKey.size(), txCredit.vout[0].nValue, (const unsigned char*)&stream[0], stream.size(), 0, libconsensus_flags, NULL, &debugger_cbs, &dbgdata) == expect, message);
+ BOOST_CHECK_MESSAGE(dbgdata.begin > 0, message);
+ BOOST_CHECK_MESSAGE(dbgdata.prestep > 0, message);
+ BOOST_CHECK_MESSAGE(dbgdata.eof > 0, message);
}
#endif
}
@@ -998,21 +1044,21 @@ BOOST_AUTO_TEST_CASE(script_PushData)
ScriptError err;
std::vector<std::vector<unsigned char> > directStack;
- BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
+ BOOST_CHECK(EvalScript(ScriptExecution::Context::Sig, directStack, CScript(&direct[0], &direct[sizeof(direct)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata1Stack;
- BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
+ BOOST_CHECK(EvalScript(ScriptExecution::Context::Sig, pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
BOOST_CHECK(pushdata1Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata2Stack;
- BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
+ BOOST_CHECK(EvalScript(ScriptExecution::Context::Sig, pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
BOOST_CHECK(pushdata2Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
std::vector<std::vector<unsigned char> > pushdata4Stack;
- BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
+ BOOST_CHECK(EvalScript(ScriptExecution::Context::Sig, pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), SCRIPT_VERIFY_P2SH, BaseSignatureChecker(), SIGVERSION_BASE, &err));
BOOST_CHECK(pushdata4Stack == directStack);
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
View
@@ -385,7 +385,7 @@ static CScript PushAll(const std::vector<valtype>& values)
void ReplaceRedeemScript(CScript& script, const CScript& redeemScript)
{
std::vector<valtype> stack;
- EvalScript(stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE);
+ EvalScript(ScriptExecution::Context::Sig, stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SIGVERSION_BASE);
assert(stack.size() > 0);
stack.back() = std::vector<unsigned char>(redeemScript.begin(), redeemScript.end());
script = PushAll(stack);