Skip to content

Commit 608c521

Browse files
authored
Merge pull request #60 from kallewoof/202010-verbose
add --verbose and despam a little
2 parents 991348d + 4e9474b commit 608c521

File tree

7 files changed

+78
-15
lines changed

7 files changed

+78
-15
lines changed

btcdeb.cpp

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
bool quiet = false;
1616
bool pipe_in = false; // xxx | btcdeb
1717
bool pipe_out = false; // btcdeb xxx > file
18+
bool verbose = false;
1819

1920
struct script_verify_flag {
2021
std::string str;
@@ -86,6 +87,18 @@ static unsigned int svf_parse_flags(unsigned int in_flags, const char* mod) {
8687
return in_flags;
8788
}
8889

90+
void setup_debug_set(const std::string& debug_params, std::set<std::string>& debug_set)
91+
{
92+
if (debug_set.empty() && !debug_params.empty()) delimiter_set(debug_params, debug_set);
93+
}
94+
95+
bool get_debug_flag(const std::string& name, const std::set<std::string>& debug_set)
96+
{
97+
// variant 1: debug parameter, lowercase, without prefix, contained in debug_set
98+
// variant 2: environment var, all caps, prefixed with 'DEBUG_'
99+
return debug_set.count(name) || std::getenv(("DEBUG_" + ToUpper(name)).c_str());
100+
}
101+
89102
int main(int argc, char* const* argv)
90103
{
91104
pipe_in = !isatty(fileno(stdin)) || std::getenv("DEBUG_SET_PIPE_IN");
@@ -101,13 +114,21 @@ int main(int argc, char* const* argv)
101114
ca.add_option("select", 's', req_arg);
102115
ca.add_option("pretend-valid", 'P', req_arg);
103116
ca.add_option("default-flags", 'd', no_arg);
104-
ca.add_option("version", 'v', no_arg);
117+
ca.add_option("version", 'V', no_arg);
105118
ca.add_option("dataset", 'X', opt_arg);
119+
ca.add_option("verbose", 'v', no_arg);
120+
ca.add_option("debug", 'D', req_arg);
106121
ca.parse(argc, argv);
107122
quiet = ca.m.count('q') || pipe_in || pipe_out;
108123

124+
btcdeb_verbose = verbose = ca.m.count('v');
125+
if (quiet && verbose) {
126+
fprintf(stderr, "You cannot both require silence and verbosity.\n");
127+
exit(1);
128+
}
129+
109130
if (ca.m.count('h')) {
110-
fprintf(stderr, "Syntax: %s [-v|--version] [-q|--quiet] [--dataset=<name>|-X<name>] [--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]);
131+
fprintf(stderr, "Syntax: %s [-V|--version] [-v|--verbose] [-q|--quiet] [--debug=[sighash|signing|segwit[,...]]|-D[sighash|...]]] [--dataset=<name>|-X<name>] [--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]);
111132
fprintf(stderr, "If executed with no arguments, an empty script and empty stack is provided\n");
112133
fprintf(stderr, "If executed with a --dataset, the --txin and --tx values are prepopulated with values from the given dataset; though this may be overridden using subsequent --tx/--txin= statements. To see available datasets, type %s --dataset or %s -X\n", argv[0], argv[0]);
113134
fprintf(stderr, "To debug transaction signatures, you need to either provide the transaction hex (the WHOLE hex, not just the txid) "
@@ -117,12 +138,13 @@ int main(int argc, char* const* argv)
117138
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");
118139
fprintf(stderr, "You can modify verification flags using the --modify-flags command. separate flags using comma (,). prefix with + to enable, - to disable. e.g. --modify-flags=\"-NULLDUMMY,-MINIMALIF\"\n");
119140
fprintf(stderr, "You can set the environment variables DEBUG_SIGHASH, DEBUG_SIGNING, and DEBUG_SEGWIT to increase verbosity for the respective areas.\n");
120-
printf("The standard (enabled by default) flags can be reviewed by typing %s --default-flags or %s -d", argv[0], argv[0]);
141+
fprintf(stderr, "The standard (enabled by default) flags can be reviewed by typing %s --default-flags or %s -d", argv[0], argv[0]);
142+
fprintf(stderr, "The --verbose flag will turn btcdeb into a helpful hintful chatter-box in various situations.\n");
121143
return 0;
122144
} else if (ca.m.count('d')) {
123145
printf("The standard (enabled by default) flags are:\n・ %s\n", svf_string(STANDARD_SCRIPT_VERIFY_FLAGS, "\n").c_str());
124146
return 0;
125-
} else if (ca.m.count('v')) {
147+
} else if (ca.m.count('V')) {
126148
printf("btcdeb (\"The Bitcoin Script Debugger\") version %d.%d.%d\n", CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION);
127149
return 0;
128150
} else if (ca.m.count('X')) {
@@ -137,32 +159,35 @@ int main(int argc, char* const* argv)
137159
// populate --tx from dataset
138160
std::string data = string_from_file(std::string("doc/txs/") + dataset + "-tx");
139161
ca.m['x'] = data;
140-
btc_logf("loaded spending transaction from dataset %s\n", dataset.c_str());
162+
if (verbose) printf("loaded spending (output) transaction from dataset %s\n", dataset.c_str());
141163
}
142164
if (!ca.m.count('i')) {
143165
// populate --txin from dataset
144166
std::string data = string_from_file(std::string("doc/txs/") + dataset + "-in");
145167
ca.m['i'] = data;
146-
btc_logf("loaded spending transaction from dataset %s\n", dataset.c_str());
168+
if (verbose) printf("loaded funding (input) transaction from dataset %s\n", dataset.c_str());
147169
}
148170
} catch (const std::runtime_error& err) {
149171
fprintf(stderr, "error loading from dataset \"%s\": %s\n", dataset.c_str(), err.what());
150172
return 1;
151173
}
152174
} else if (!quiet) {
153-
btc_logf("btcdeb %d.%d.%d -- type `%s -h` for start up options\n", CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, argv[0]);
175+
printf("btcdeb %d.%d.%d -- type `%s -h` for start up options\n", CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, argv[0]);
154176
}
155177

156178
if (!pipe_in) {
157-
if (std::getenv("DEBUG_SIGHASH")) btc_sighash_logf = btc_logf_stderr;
158-
if (std::getenv("DEBUG_SIGNING")) btc_sign_logf = btc_logf_stderr;
159-
if (std::getenv("DEBUG_SEGWIT")) btc_segwit_logf = btc_logf_stderr;
179+
std::set<std::string> debug_set;
180+
setup_debug_set(ca.m['D'], debug_set);
181+
if (get_debug_flag("sighash", debug_set)) btc_sighash_logf = btc_logf_stderr;
182+
if (get_debug_flag("signing", debug_set)) btc_sign_logf = btc_logf_stderr;
183+
if (get_debug_flag("segwit", debug_set)) btc_segwit_logf = btc_logf_stderr;
184+
btc_logf("notice: btcdeb has gotten quieter; use --verbose if necessary (this message is temporary)\n");
160185
}
161186

162187
unsigned int flags = STANDARD_SCRIPT_VERIFY_FLAGS;
163188
if (ca.m.count('f')) {
164189
flags = svf_parse_flags(flags, ca.m['f'].c_str());
165-
if (!quiet) fprintf(stderr, "resulting flags:\n・ %s\n", svf_string(flags, "\n").c_str());
190+
if (verbose) fprintf(stderr, "resulting flags:\n・ %s\n", svf_string(flags, "\n").c_str());
166191
}
167192

168193
int selected = -1;
@@ -181,13 +206,13 @@ int main(int argc, char* const* argv)
181206
if (!instance.parse_transaction(ca.m['x'].c_str(), true)) {
182207
return 1;
183208
}
184-
if (!quiet) fprintf(stderr, "got %stransaction %s:\n%s\n", instance.sigver == SigVersion::WITNESS_V0 ? "segwit " : "", instance.tx->GetHash().ToString().c_str(), instance.tx->ToString().c_str());
209+
if (verbose) fprintf(stderr, "got %stransaction %s:\n%s\n", instance.sigver == SigVersion::WITNESS_V0 ? "segwit " : "", instance.tx->GetHash().ToString().c_str(), instance.tx->ToString().c_str());
185210
}
186211
if (ca.m.count('i')) {
187212
if (!instance.parse_input_transaction(ca.m['i'].c_str(), selected)) {
188213
return 1;
189214
}
190-
if (!quiet) fprintf(stderr, "got input tx #%" PRId64 " %s:\n%s\n", instance.txin_index, instance.txin->GetHash().ToString().c_str(), instance.txin->ToString().c_str());
215+
if (verbose) fprintf(stderr, "got input tx #%" PRId64 " %s:\n%s\n", instance.txin_index, instance.txin->GetHash().ToString().c_str(), instance.txin->ToString().c_str());
191216
}
192217
char* script_str = nullptr;
193218
if (pipe_in) {
@@ -212,7 +237,7 @@ int main(int argc, char* const* argv)
212237
CScript script;
213238
if (script_str) {
214239
if (instance.parse_script(script_str)) {
215-
if (!quiet) btc_logf("valid script\n");
240+
if (verbose) btc_logf("valid script\n");
216241
} else {
217242
fprintf(stderr, "invalid script\n");
218243
return 1;
@@ -289,6 +314,14 @@ int main(int argc, char* const* argv)
289314
}
290315
}
291316

317+
if (instance.has_preamble) {
318+
if (verbose) btc_logf(
319+
"*** note: there is a for-clarity preamble\n\n"
320+
321+
"This is a virtual script that btcdeb generates and presents to you so you can step through the validation process one step at a time. The input is simply the redeem script hash, whereas btcdeb presents it as a OP_DUP, OP_HASH160, <that hash>, OP_EQUALVERIFY script.\n"
322+
); else if (!quiet) btc_logf("note: there is a for-clarity preamble (use --verbose for details)\n");
323+
}
324+
292325
if (pipe_in || pipe_out) {
293326
if (!ContinueScript(*env)) {
294327
fprintf(stderr, "error: %s\n", ScriptErrorString(*env->serror).c_str());

cliargs.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77

88
#include <map>
99
#include <vector>
10+
#include <set>
1011

12+
#include <cstring>
1113
#include <getopt.h>
1214

1315
#include <tinyformat.h>
@@ -94,4 +96,20 @@ std::string string_from_file(const std::string& path) {
9496
return r;
9597
}
9698

99+
/**
100+
* Parse a comma and/or space separated list of inputs into an existing set.
101+
*/
102+
inline void delimiter_set(const std::string& input, std::set<std::string>& output)
103+
{
104+
size_t len = input.size();
105+
std::string s;
106+
for (size_t j = 0; j <= len; ++j) {
107+
if (j == len || input[j] == ',' || input[j] == ' ') {
108+
if (s.empty()) continue;
109+
output.insert(s);
110+
s.clear();
111+
} else s += tolower(input[j]);
112+
}
113+
}
114+
97115
#endif // BITCOIN_CLIARGS_H

debugger/script.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <util/strencodings.h>
88
#include <cstdarg>
99

10+
bool btcdeb_verbose{false};
1011
void btc_logf_dummy(const char* fmt...) {}
1112
void btc_logf_stderr(const char* fmt...) {
1213
va_list args;

debugger/script.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
typedef void (*btc_logf_t) (const char *fmt...);
1111
extern btc_logf_t btc_logf, btc_sighash_logf, btc_sign_logf, btc_segwit_logf;
12+
extern bool btcdeb_verbose;
1213
void btc_logf_dummy(const char* fmt...);
1314
void btc_logf_stderr(const char* fmt...);
1415
inline bool btc_enabled(btc_logf_t logger) { return logger != btc_logf_dummy; }

instance.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,8 @@ bool Instance::configure_tx_txin() {
454454
size_t wstack_to_stack = wstack.size();
455455
if (!wsh) {
456456
validation = CScript() << OP_DUP << OP_HASH160 << program << OP_EQUALVERIFY << OP_CHECKSIG;
457+
// this is the preamble; it is btcdeb pretending that a script exists which doesn't
458+
has_preamble = true;
457459
} else {
458460
wstack_to_stack--; // do not include the script on the stack
459461
validation = CScript(wstack.back().begin(), wstack.back().end());

instance.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class Instance {
3838
*/
3939
std::map<valtype,valtype> pretend_valid_map;
4040
std::set<valtype> pretend_valid_pubkeys;
41+
bool has_preamble;
4142

4243
Instance()
4344
: env(nullptr)
@@ -46,6 +47,7 @@ class Instance {
4647
, txin_vout_index(-1)
4748
, sigver(SigVersion::BASE)
4849
, checker(nullptr)
50+
, has_preamble(false)
4951
{}
5052

5153
~Instance() {

script/interpreter.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1115,7 +1115,13 @@ bool StepScript(ScriptExecutionEnvironment& env, CScript::const_iterator& pc, CS
11151115
if (stack.size() < 1)
11161116
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
11171117
if ((flags & SCRIPT_VERIFY_NULLDUMMY) && stacktop(-1).size()) {
1118-
printf("\n* * * * * * *\n\nHint: with Segwit activation, the OP_CHECKMULTISIG extra argument must be set to the empty push value (0x). If you are experimenting with older scripts, you may run into a 'Dummy CHECKMULTISIG argument must be zero' error. To move past this, you need to modify the verification flags, disabling the NULLDUMMY flag specifically. I.e. call btcdeb again with -f\"-NULLDUMMY\"\n\n* * * * * * *\n\n");
1118+
if (btcdeb_verbose) printf(
1119+
"\n* * * * * * *\n\n"
1120+
1121+
"Hint: with Segwit activation, the OP_CHECKMULTISIG extra argument must be set to the empty push value (0x). If you are experimenting with older scripts, you may run into a 'Dummy CHECKMULTISIG argument must be zero' error. To move past this, you need to modify the verification flags, disabling the NULLDUMMY flag specifically. I.e. call btcdeb again with -f\"-NULLDUMMY\"\n\n"
1122+
1123+
"* * * * * * *\n\n"
1124+
); else printf("Hint: with Segwit activation, the OP_CHECKMULTISIG extra argument must be empty (--verbose for details)\n");
11191125
return set_error(serror, SCRIPT_ERR_SIG_NULLDUMMY);
11201126
}
11211127
popstack(stack);

0 commit comments

Comments
 (0)