cbe39a3852
This is a simple utility that provides command line manipulation of a hex-encoded TX. The utility takes a hex string on the command line as input, performs zero or more mutations, and outputs a hex string to standard output. This utility is also an intentional exercise of the "bitcoin library" concept. It is designed to require minimal libraries, and works entirely without need for any RPC or P2P communication. See "bitcoin-tx --help" for command and options summary.
128 lines
3.6 KiB
C++
128 lines
3.6 KiB
C++
|
|
#include <vector>
|
|
#include "core_io.h"
|
|
#include "core.h"
|
|
#include "serialize.h"
|
|
#include "script.h"
|
|
#include "util.h"
|
|
|
|
#include <boost/assign/list_of.hpp>
|
|
#include <boost/algorithm/string/classification.hpp>
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
#include <boost/algorithm/string/split.hpp>
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
#include "univalue/univalue.h"
|
|
|
|
using namespace std;
|
|
using namespace boost;
|
|
using namespace boost::algorithm;
|
|
|
|
CScript ParseScript(std::string s)
|
|
{
|
|
CScript result;
|
|
|
|
static map<string, opcodetype> mapOpNames;
|
|
|
|
if (mapOpNames.empty())
|
|
{
|
|
for (int op = 0; op <= OP_NOP10; op++)
|
|
{
|
|
// Allow OP_RESERVED to get into mapOpNames
|
|
if (op < OP_NOP && op != OP_RESERVED)
|
|
continue;
|
|
|
|
const char* name = GetOpName((opcodetype)op);
|
|
if (strcmp(name, "OP_UNKNOWN") == 0)
|
|
continue;
|
|
string strName(name);
|
|
mapOpNames[strName] = (opcodetype)op;
|
|
// Convenience: OP_ADD and just ADD are both recognized:
|
|
replace_first(strName, "OP_", "");
|
|
mapOpNames[strName] = (opcodetype)op;
|
|
}
|
|
}
|
|
|
|
vector<string> words;
|
|
split(words, s, is_any_of(" \t\n"), token_compress_on);
|
|
|
|
for (std::vector<std::string>::const_iterator w = words.begin(); w != words.end(); ++w)
|
|
{
|
|
if (w->empty())
|
|
{
|
|
// Empty string, ignore. (boost::split given '' will return one word)
|
|
}
|
|
else if (all(*w, is_digit()) ||
|
|
(starts_with(*w, "-") && all(string(w->begin()+1, w->end()), is_digit())))
|
|
{
|
|
// Number
|
|
int64_t n = atoi64(*w);
|
|
result << n;
|
|
}
|
|
else if (starts_with(*w, "0x") && (w->begin()+2 != w->end()) && IsHex(string(w->begin()+2, w->end())))
|
|
{
|
|
// Raw hex data, inserted NOT pushed onto stack:
|
|
std::vector<unsigned char> raw = ParseHex(string(w->begin()+2, w->end()));
|
|
result.insert(result.end(), raw.begin(), raw.end());
|
|
}
|
|
else if (w->size() >= 2 && starts_with(*w, "'") && ends_with(*w, "'"))
|
|
{
|
|
// Single-quoted string, pushed as data. NOTE: this is poor-man's
|
|
// parsing, spaces/tabs/newlines in single-quoted strings won't work.
|
|
std::vector<unsigned char> value(w->begin()+1, w->end()-1);
|
|
result << value;
|
|
}
|
|
else if (mapOpNames.count(*w))
|
|
{
|
|
// opcode, e.g. OP_ADD or ADD:
|
|
result << mapOpNames[*w];
|
|
}
|
|
else
|
|
{
|
|
throw runtime_error("script parse error");
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx)
|
|
{
|
|
if (!IsHex(strHexTx))
|
|
return false;
|
|
|
|
vector<unsigned char> txData(ParseHex(strHexTx));
|
|
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
|
try {
|
|
ssData >> tx;
|
|
}
|
|
catch (std::exception &e) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint256 ParseHashUV(const UniValue& v, const string& strName)
|
|
{
|
|
string strHex;
|
|
if (v.isStr())
|
|
strHex = v.getValStr();
|
|
if (!IsHex(strHex)) // Note: IsHex("") is false
|
|
throw runtime_error(strName+" must be hexadecimal string (not '"+strHex+"')");
|
|
|
|
uint256 result;
|
|
result.SetHex(strHex);
|
|
return result;
|
|
}
|
|
|
|
vector<unsigned char> ParseHexUV(const UniValue& v, const string& strName)
|
|
{
|
|
string strHex;
|
|
if (v.isStr())
|
|
strHex = v.getValStr();
|
|
if (!IsHex(strHex))
|
|
throw runtime_error(strName+" must be hexadecimal string (not '"+strHex+"')");
|
|
return ParseHex(strHex);
|
|
}
|
|
|