Skip to content

Commit

Permalink
add --pretend-valid option
Browse files Browse the repository at this point in the history
  • Loading branch information
kallewoof committed Jan 17, 2020
1 parent a269dc4 commit 4de9e60
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 10 deletions.
13 changes: 10 additions & 3 deletions btcdeb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,16 @@ int main(int argc, char* const* argv)
ca.add_option("txin", 'i', req_arg);
ca.add_option("modify-flags", 'f', req_arg);
ca.add_option("select", 's', req_arg);
ca.add_option("pretend-valid", 'P', req_arg);
ca.add_option("default-flags", 'd', no_arg);
ca.parse(argc, argv);
quiet = ca.m.count('q') || pipe_in || pipe_out;

if (ca.m.count('h')) {
fprintf(stderr, "Syntax: %s [-q|--quiet] [--tx=[amount1,amount2,..:]<hex> [--txin=<hex>] [--modify-flags=<flags>|-f<flags>] [--select=<index>|-s<index>] [<script> [<stack bottom item> [... [<stack top item>]]]]]\n", argv[0]);
fprintf(stderr, "Syntax: %s [-q|--quiet] [--tx=[amount1,amount2,..:]<hex> [--txin=<hex>] [--modify-flags=<flags>|-f<flags>] [--select=<index>|-s<index>] [--pretend-valid=<sig>:<pubkey>[,<sig2>:<pubkey2>[,...]]|-P<sig>:<pubkey>[,...]] [<script> [<stack bottom item> [... [<stack top item>]]]]]\n", argv[0]);
fprintf(stderr, "If executed with no arguments, an empty script and empty stack is provided\n");
fprintf(stderr, "To debug transaction signatures, you need to provide the transaction hex (the WHOLE hex, not just the txid) "
"as well as (SegWit only) every amount for the inputs\n");
fprintf(stderr, "To debug transaction signatures, you need to either provide the transaction hex (the WHOLE hex, not just the txid) "
"as well as (SegWit only) every amount for the inputs, or provide (one or more) signature:pubkey pairs using --pretend-valid\n");
fprintf(stderr, "E.g. if a SegWit transaction abc123... has 2 inputs of 0.1 btc and 0.002 btc, you would do tx=0.1,0.002:abc123...\n");
fprintf(stderr, "You do not need the amounts for non-SegWit transactions\n");
fprintf(stderr, "By providing a txin as well as a tx and no script or stack, btcdeb will attempt to set up a debug session for the verification of the given input by pulling the appropriate values out of the respective transactions. you do not need amounts for --tx in this case\n");
Expand Down Expand Up @@ -182,6 +183,12 @@ int main(int argc, char* const* argv)

instance.parse_stack_args(ca.l);

if (ca.m.count('P')) {
if (!instance.parse_pretend_valid_expr(ca.m['P'].c_str())) {
return 1;
}
}

if (instance.txin && instance.tx && ca.l.size() == 0 && instance.script.size() == 0) {
if (!instance.configure_tx_txin()) return 1;
}
Expand Down
37 changes: 37 additions & 0 deletions instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,41 @@ bool Instance::parse_script(const std::vector<uint8_t>& script_data) {
return script.HasValidOps();
}

bool Instance::parse_pretend_valid_expr(const char* expr) {
const char* p = expr;
const char* c = p;
valtype key;
bool got_key = false;
while (*c) {
while (*c && *c != ',' && *c != ':') ++c;
char* cs = strndup(p, c-p);
valtype s = Value(cs).data_value();
free(cs);
switch (*c) {
case ':':
if (got_key) {
fprintf(stderr, "parse error (unexpected colon) near %s\n", p);
return false;
}
key = s;
got_key = true;
break;
case ',':
case 0:
if (!got_key) {
fprintf(stderr, "parse error (missing key) near %s\n", p);
return false;
}
got_key = false;
pretend_valid_map[key] = s;
pretend_valid_pubkeys.insert(s);
break;
}
p = c = c + (*c != 0);
}
return true;
}

void Instance::parse_stack_args(const std::vector<const char*> args) {
for (auto& v : args) {
stack.push_back(Value(v).data_value());
Expand All @@ -125,6 +160,8 @@ bool Instance::setup_environment(unsigned int flags) {

env = new InterpreterEnv(stack, script, flags, *checker, sigver, &error);
env->successor_script = successor_script;
env->pretend_valid_map = pretend_valid_map;
env->pretend_valid_pubkeys = pretend_valid_pubkeys;
env->done &= successor_script.size() == 0;

return env->operational;
Expand Down
12 changes: 12 additions & 0 deletions instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <pubkey.h>
#include <value.h>
#include <vector>
#include <map>

typedef std::vector<unsigned char> valtype;

Expand All @@ -28,6 +29,15 @@ class Instance {
BaseSignatureChecker* checker;
ScriptError error;
std::string exception_string = "";
/**
* Mapping of pubkeys to signatures that should be considered cryptographically valid.
*
* For the keypair <key>=<value>, if a signature <value> is checked against a pubkey <key>
* for any signature hash <hash>, the cryptographic check is skipped and the signature is
* assumed to be valid.
*/
std::map<valtype,valtype> pretend_valid_map;
std::set<valtype> pretend_valid_pubkeys;

Instance()
: env(nullptr)
Expand All @@ -52,6 +62,8 @@ class Instance {
void parse_stack_args(size_t argc, char* const* argv, size_t starting_index);
void parse_stack_args(const std::vector<const char*> args);

bool parse_pretend_valid_expr(const char* expr);

bool configure_tx_txin();

bool setup_environment(unsigned int flags = STANDARD_SCRIPT_VERIFY_FLAGS);
Expand Down
30 changes: 26 additions & 4 deletions script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ bool StepScript(ScriptExecutionEnvironment& env, CScriptIter& pc, CScript* local
auto& checker = env.checker;
auto& sigversion = env.sigversion;
auto& serror = env.serror;
auto& pretend_valid_map = env.pretend_valid_map;
auto& pretend_valid_pubkeys = env.pretend_valid_pubkeys;

bool fExec = !count(vfExec.begin(), vfExec.end(), false);

Expand Down Expand Up @@ -906,11 +908,31 @@ bool StepScript(ScriptExecutionEnvironment& env, CScriptIter& pc, CScript* local
return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE);
}

if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
//serror is set
return false;
bool fSuccess;
std::string sig_str = HexStr(vchSig);
std::string pub_str = HexStr(vchPubKey);
if (pretend_valid_pubkeys.count(vchPubKey)) {
fSuccess = pretend_valid_map.count(vchSig) && pretend_valid_map.at(vchSig) == vchPubKey;
if (!fSuccess) {
fprintf(stderr, "note: pretend signature mismatch: got %s=%s, expected %s=%s\n",
sig_str.c_str(), pub_str.c_str(),
pretend_valid_map.count(vchSig) ? HexStr(pretend_valid_map.at(vchSig)).c_str() : "<null>",
pub_str.c_str()
);
}
} else {
if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) {
//serror is set
if (pretend_valid_map.size() > 0) {
fprintf(stderr, "note: pubkey not found in pretend set: %s not in (%s)\n", pub_str.c_str(), Join<std::set<valtype>,std::vector<unsigned char>>(pretend_valid_pubkeys, ", ", JoinHexStrFun).c_str());
}
return false;
}
fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);
if (!fSuccess && pretend_valid_map.size() > 0) {
fprintf(stderr, "note: pubkey not found in pretend set: %s not in (%s)\n", pub_str.c_str(), Join<std::set<valtype>,std::vector<unsigned char>>(pretend_valid_pubkeys, ", ", JoinHexStrFun).c_str());
}
}
bool fSuccess = checker.CheckSig(vchSig, vchPubKey, scriptCode, sigversion);

if (!fSuccess && (flags & SCRIPT_VERIFY_NULLFAIL) && vchSig.size())
return set_error(serror, SCRIPT_ERR_SIG_NULLFAIL);
Expand Down
2 changes: 2 additions & 0 deletions script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ struct ScriptExecutionEnvironment {
const BaseSignatureChecker& checker;
SigVersion sigversion;
ScriptError* serror;
std::map<std::vector<unsigned char>,std::vector<unsigned char>> pretend_valid_map;
std::set<std::vector<unsigned char>> pretend_valid_pubkeys;
ScriptExecutionEnvironment(std::vector<std::vector<unsigned char> >& stack_in, const CScript& script_in, unsigned int flags_in, const BaseSignatureChecker& checker_in);
};

Expand Down
13 changes: 13 additions & 0 deletions utilstrencodings.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <stdint.h>
#include <string>
#include <vector>
#include <functional>

#define BEGIN(a) ((char*)&(a))
#define END(a) ((char*)&((&(a))[1]))
Expand Down Expand Up @@ -149,6 +150,18 @@ bool TimingResistantEqual(const T& a, const T& b)
*/
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);

template <typename Tset, typename Tel>
inline std::string Join(const Tset& iterable, const std::string& sep,
std::function<std::string(const Tel&)> strfun) {
std::string rv = "";
for (const Tel& el : iterable) {
rv += (rv[0] ? ", " : "") + strfun(el);
}
return rv;
}

inline std::string JoinHexStrFun(const std::vector<unsigned char>& t) { return HexStr(t); }

/** Convert from one power-of-2 number base to another. */
template<int frombits, int tobits, bool pad, typename O, typename I>
bool ConvertBits(O& out, I it, I end) {
Expand Down
5 changes: 2 additions & 3 deletions value.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,8 @@ struct Value {
return HexStr(CScriptNum::serialize(int64));
case T_DATA:
return HexStr(data);
default:
fprintf(stderr, "cannot convert string into hex value: %s\n", str.c_str());
return "";
case T_STRING:
return HexStr(data_value());
}
}
int64_t int_value() const {
Expand Down

0 comments on commit 4de9e60

Please sign in to comment.