Publish Nerbus source snapshot and build documentation

This commit is contained in:
Nerbus Contributors committed 2026-10-17 18:56:27 +00:00
commit 73401987df
17 files changed
+2169

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
Copyright (c) 2009-2017 The Bitcoin Core developers
Copyright (c) 2009-2018 Bitcoin Developers
Copyright (c) 2016-2024 The Zcash developers
Copyright (c) 2016-2025 The Komodo developers
Copyright (c) 2018-2025 The VerusCoin developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
The MIT software license (https://www.opensource.org/licenses/mit-license.php)
above applies to the code directly included in this source distribution, with
the exception of certain Autoconf macros. Dependencies downloaded as part of
the build process may be covered by other open-source licenses. The MIT-licensed
source code is not considered a derived work of these Autoconf macros or of the
dependencies. For further details see 'contrib/debian/copyright'.
This product includes software developed by the OpenSSL Project for use in the
OpenSSL Toolkit (https://www.openssl.org/). This product includes cryptographic
software written by Eric Young (eay@cryptsoft.com).
Although almost all of the Zcash/Komodo/VerusCoin code is licensed under "permissive" open source
licenses, users and distributors should note that when built using the default
build options, Verus depends on Oracle Berkeley DB 6.2.x, which is licensed
under the GNU Affero General Public License. Additionally, src/pbaas/shuffle_compat.h, which
is used solely for Apple Mac platform builds, contains source code derived from GCC libstdc++
(Copyright 2009-2023 Free Software Foundation) for consensus compatibility when building
without libstdc++, also governed by GPLv3.
Contributors should understand licensing implications before modifying the
following files in build-aux/m4 (see https://github.com/zcash/zcash/issues/2827):
* ax_check_compile_flag.m4
* ax_check_link_flag.m4
* ax_check_preproc_flag.m4
* ax_pthread.m4
+729
View File
@@ -0,0 +1,729 @@
// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php .
#include "clientversion.h"
#include "coins.h"
#include "consensus/consensus.h"
#include "consensus/upgrades.h"
#include "core_io.h"
#include "key_io.h"
#include "keystore.h"
#include "primitives/transaction.h"
#include "script/script.h"
#include "pbaas/pbaas.h"
#include "script/sign.h"
#include <univalue.h>
#include "util.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
#include <stdio.h>
#include <boost/algorithm/string.hpp>
#include <boost/assign/list_of.hpp>
using namespace std;
#include "uint256.h"
#include "arith_uint256.h"
#include "komodo_structs.h"
#include "komodo_globals.h"
#include "komodo_defs.h"
#include "komodo_interest.h"
struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
{
return NULL;
}
uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight)
{
return(0);
}
CConnectedChains ConnectedChains;
CCurrencyDefinition CConnectedChains::GetCachedCurrency(const uint160 &currencyID)
{
assert(false);
return CCurrencyDefinition();
}
// unused in client, but here for linking
bool fIdIndex;
bool fConversionIndex;
uint160 CIdentity::IdentityPrimaryAddressKey(const CTxDestination &dest)
{
assert(false);
return uint160();
}
static bool fCreateBlank;
static std::map<std::string,UniValue> registers;
static const int CONTINUE_EXECUTION=-1;
boost::optional<libzcash::SaplingPaymentAddress> defaultSaplingDest;
//
// This function returns either one of EXIT_ codes when it's expected to stop the process or
// CONTINUE_EXECUTION when it's expected to continue further.
//
static int AppInitRawTx(int argc, char* argv[])
{
//
// Parameters
//
ParseParameters(argc, argv);
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
if (!SelectParamsFromCommandLine()) {
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
return false;
}
fCreateBlank = GetBoolArg("-create", false);
if (argc<2 || IsArgSet("-?") || IsArgSet("-h") || IsArgSet("-help"))
{
// First part of help message is specific to this utility
std::string strUsage = _("Nerbus nerbus-tx utility version") + " " + FormatFullVersion() + "\n\n" +
_("Usage:") + "\n" +
" nerbus-tx [options] <hex-tx> [commands] " + _("Update hex-encoded Nerbus transaction") + "\n" +
" nerbus-tx [options] -create [commands] " + _("Create hex-encoded Nerbus transaction") + "\n" +
"\n";
fprintf(stdout, "%s", strUsage.c_str());
strUsage = HelpMessageGroup(_("Options:"));
strUsage += HelpMessageOpt("-?", _("This help message"));
strUsage += HelpMessageOpt("-create", _("Create new, empty TX."));
strUsage += HelpMessageOpt("-json", _("Select JSON output"));
strUsage += HelpMessageOpt("-txid", _("Output only the hex-encoded transaction id of the resultant transaction."));
strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be solved instantly."));
strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
fprintf(stdout, "%s", strUsage.c_str());
strUsage = HelpMessageGroup(_("Commands:"));
strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX"));
strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX"));
strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX"));
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX"));
strUsage += HelpMessageOpt("sign=HEIGHT:SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
_("This command requires JSON registers:") +
_("prevtxs=JSON object") + ", " +
_("privatekeys=JSON object") + ". " +
_("See signrawtransaction docs for format of sighash flags, JSON objects."));
fprintf(stdout, "%s", strUsage.c_str());
strUsage = HelpMessageGroup(_("Register Commands:"));
strUsage += HelpMessageOpt("load=NAME:FILENAME", _("Load JSON file FILENAME into register NAME"));
strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"));
fprintf(stdout, "%s", strUsage.c_str());
if (argc < 2) {
fprintf(stderr, "Error: too few parameters\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
return CONTINUE_EXECUTION;
}
static void RegisterSetJson(const std::string& key, const std::string& rawJson)
{
UniValue val;
if (!val.read(rawJson)) {
std::string strErr = "Cannot parse JSON for key " + key;
throw std::runtime_error(strErr);
}
registers[key] = val;
}
static void RegisterSet(const std::string& strInput)
{
// separate NAME:VALUE in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw std::runtime_error("Register input requires NAME:VALUE");
std::string key = strInput.substr(0, pos);
std::string valStr = strInput.substr(pos + 1, std::string::npos);
RegisterSetJson(key, valStr);
}
static void RegisterLoad(const std::string& strInput)
{
// separate NAME:FILENAME in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw std::runtime_error("Register load requires NAME:FILENAME");
std::string key = strInput.substr(0, pos);
std::string filename = strInput.substr(pos + 1, std::string::npos);
FILE *f = fopen(filename.c_str(), "r");
if (!f) {
std::string strErr = "Cannot open file " + filename;
throw std::runtime_error(strErr);
}
// load file chunks into one big buffer
std::string valStr;
while ((!feof(f)) && (!ferror(f))) {
char buf[4096];
int bread = fread(buf, 1, sizeof(buf), f);
if (bread <= 0)
break;
valStr.insert(valStr.size(), buf, bread);
}
int error = ferror(f);
fclose(f);
if (error) {
std::string strErr = "Error reading file " + filename;
throw std::runtime_error(strErr);
}
// evaluate as JSON buffer register
RegisterSetJson(key, valStr);
}
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
{
int64_t newVersion = atoi64(cmdVal);
if (newVersion < CTransaction::SPROUT_MIN_CURRENT_VERSION || newVersion > CTransaction::SPROUT_MAX_CURRENT_VERSION)
throw std::runtime_error("Invalid TX version requested");
tx.nVersion = (int) newVersion;
}
static void MutateTxExpiry(CMutableTransaction& tx, const std::string& cmdVal)
{
int64_t newExpiry = atoi64(cmdVal);
if (newExpiry <= 0 || newExpiry >= TX_EXPIRY_HEIGHT_THRESHOLD) {
throw std::runtime_error("Invalid TX expiry requested");
}
tx.nExpiryHeight = (int) newExpiry;
}
static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
{
int64_t newLocktime = atoi64(cmdVal);
if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
throw std::runtime_error("Invalid TX locktime requested");
tx.nLockTime = (unsigned int) newLocktime;
}
static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
{
std::vector<std::string> vStrInputParts;
boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
// separate TXID:VOUT in string
if (vStrInputParts.size()<2)
throw std::runtime_error("TX input missing separator");
// extract and validate TXID
std::string strTxid = vStrInputParts[0];
if ((strTxid.size() != 64) || !IsHex(strTxid))
throw std::runtime_error("invalid TX input txid");
uint256 txid(uint256S(strTxid));
static const unsigned int minTxOutSz = 9;
static const unsigned int maxVout = MAX_BLOCK_SIZE / minTxOutSz;
// extract and validate vout
std::string strVout = vStrInputParts[1];
int vout = atoi(strVout);
if ((vout < 0) || (vout > (int)maxVout))
throw std::runtime_error("invalid TX input vout");
// extract the optional sequence number
uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max();
if (vStrInputParts.size() > 2)
nSequenceIn = atoi(vStrInputParts[2]);
// append to transaction input list
CTxIn txin(txid, vout, CScript(), nSequenceIn);
tx.vin.push_back(txin);
}
static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
{
// separate VALUE:ADDRESS in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) ||
(pos == 0) ||
(pos == (strInput.size() - 1)))
throw std::runtime_error("TX output missing separator");
// extract and validate VALUE
std::string strValue = strInput.substr(0, pos);
CAmount value;
if (!ParseMoney(strValue, value))
throw std::runtime_error("invalid TX output value");
// extract and validate ADDRESS
std::string strAddr = strInput.substr(pos + 1, std::string::npos);
CTxDestination destination = DecodeDestination(strAddr);
if (!IsValidDestination(destination)) {
throw std::runtime_error("invalid TX output address");
}
CScript scriptPubKey = GetScriptForDestination(destination);
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
{
// separate VALUE:SCRIPT in string
size_t pos = strInput.find(':');
if ((pos == std::string::npos) ||
(pos == 0))
throw std::runtime_error("TX output missing separator");
// extract and validate VALUE
std::string strValue = strInput.substr(0, pos);
CAmount value;
if (!ParseMoney(strValue, value))
throw std::runtime_error("invalid TX output value");
// extract and validate script
std::string strScript = strInput.substr(pos + 1, std::string::npos);
CScript scriptPubKey = ParseScript(strScript); // throws on err
// construct TxOut, append to transaction output list
CTxOut txout(value, scriptPubKey);
tx.vout.push_back(txout);
}
static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
{
// parse requested deletion index
int inIdx = atoi(strInIdx);
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
std::string strErr = "Invalid TX input index '" + strInIdx + "'";
throw std::runtime_error(strErr.c_str());
}
// delete input from transaction
tx.vin.erase(tx.vin.begin() + inIdx);
}
static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
{
// parse requested deletion index
int outIdx = atoi(strOutIdx);
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
std::string strErr = "Invalid TX output index '" + strOutIdx + "'";
throw std::runtime_error(strErr.c_str());
}
// delete output from transaction
tx.vout.erase(tx.vout.begin() + outIdx);
}
static const unsigned int N_SIGHASH_OPTS = 6;
static const struct {
const char *flagStr;
int flags;
} sighashOptions[N_SIGHASH_OPTS] = {
{"ALL", SIGHASH_ALL},
{"NONE", SIGHASH_NONE},
{"SINGLE", SIGHASH_SINGLE},
{"ALL|ANYONECANPAY", SIGHASH_ALL|SIGHASH_ANYONECANPAY},
{"NONE|ANYONECANPAY", SIGHASH_NONE|SIGHASH_ANYONECANPAY},
{"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
};
static bool findSighashFlags(int& flags, const std::string& flagStr)
{
flags = 0;
for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
if (flagStr == sighashOptions[i].flagStr) {
flags = sighashOptions[i].flags;
return true;
}
}
return false;
}
uint256 ParseHashUO(std::map<std::string,UniValue>& o, std::string strKey)
{
if (!o.count(strKey))
return uint256();
return ParseHashUV(o[strKey], strKey);
}
std::vector<unsigned char> ParseHexUO(std::map<std::string,UniValue>& o, std::string strKey)
{
if (!o.count(strKey)) {
std::vector<unsigned char> emptyVec;
return emptyVec;
}
return ParseHexUV(o[strKey], strKey);
}
static void MutateTxSign(CMutableTransaction& tx, const std::string& strInput)
{
// separate HEIGHT:SIGHASH-FLAGS in string
size_t pos = strInput.find(':');
if ((pos == 0) ||
(pos == (strInput.size() - 1)))
throw std::runtime_error("Invalid sighash flag separator");
// extract and validate HEIGHT
std::string strHeight = strInput.substr(0, pos);
int nHeight = atoi(strHeight);
if (nHeight <= 0) {
throw std::runtime_error("invalid height");
}
// extract and validate SIGHASH-FLAGS
int nHashType = SIGHASH_ALL;
std::string flagStr;
if (pos != std::string::npos) {
flagStr = strInput.substr(pos + 1, std::string::npos);
}
if (flagStr.size() > 0)
if (!findSighashFlags(nHashType, flagStr))
throw std::runtime_error("unknown sighash flag/sign option");
std::vector<CTransaction> txVariants;
txVariants.push_back(tx);
// mergedTx will end up with all the signatures; it
// starts as a clone of the raw tx:
CMutableTransaction mergedTx(txVariants[0]);
bool fComplete = true;
CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy);
if (!registers.count("privatekeys"))
throw std::runtime_error("privatekeys register variable must be set.");
bool fGivenKeys = false;
CBasicKeyStore tempKeystore;
UniValue keysObj = registers["privatekeys"];
fGivenKeys = true;
for (size_t kidx = 0; kidx < keysObj.size(); kidx++) {
if (!keysObj[kidx].isStr())
throw std::runtime_error("privatekey not a std::string");
CKey key = DecodeSecret(keysObj[kidx].getValStr());
if (!key.IsValid()) {
throw std::runtime_error("privatekey not valid");
}
tempKeystore.AddKey(key);
}
// Add previous txouts given in the RPC call:
if (!registers.count("prevtxs"))
throw std::runtime_error("prevtxs register variable must be set.");
UniValue prevtxsObj = registers["prevtxs"];
{
for (size_t previdx = 0; previdx < prevtxsObj.size(); previdx++) {
UniValue prevOut = prevtxsObj[previdx];
if (!prevOut.isObject())
throw std::runtime_error("expected prevtxs internal object");
std::map<std::string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
if (!prevOut.checkObject(types))
throw std::runtime_error("prevtxs internal object typecheck fail");
uint256 txid = ParseHashUV(prevOut["txid"], "txid");
int nOut = atoi(prevOut["vout"].getValStr());
if (nOut < 0)
throw std::runtime_error("vout must be positive");
std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
{
CCoinsModifier coins = view.ModifyCoins(txid);
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
std::string err("Previous output scriptPubKey mismatch:\n");
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
ScriptToAsmStr(scriptPubKey);
throw std::runtime_error(err);
}
if ((unsigned int)nOut >= coins->vout.size())
coins->vout.resize(nOut+1);
coins->vout[nOut].scriptPubKey = scriptPubKey;
coins->vout[nOut].nValue = 0;
if (prevOut.exists("amount")) {
coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]);
}
}
// if redeemScript given and private keys given,
// add redeemScript to the tempKeystore so it can be signed:
if (fGivenKeys && scriptPubKey.IsPayToScriptHash() &&
prevOut.exists("redeemScript")) {
UniValue v = prevOut["redeemScript"];
std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
CScript redeemScript(rsData.begin(), rsData.end());
tempKeystore.AddCScript(redeemScript);
}
}
}
const CKeyStore& keystore = tempKeystore;
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Grab the consensus branch ID for the given height
auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
// Sign what we can:
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
CTxIn& txin = mergedTx.vin[i];
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
if (!coins || !coins->IsAvailable(txin.prevout.n)) {
fComplete = false;
continue;
}
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
const CAmount& amount = coins->vout[txin.prevout.n].nValue;
SignatureData sigdata;
// Only sign SIGHASH_SINGLE if there's a corresponding output:
if (!fHashSingle || (i < mergedTx.vout.size()))
ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, prevPubKey, nHeight, nHashType), prevPubKey, sigdata, consensusBranchId);
// ... and merge in other signatures:
BOOST_FOREACH(const CTransaction& txv, txVariants)
sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i), consensusBranchId);
UpdateTransaction(mergedTx, i, sigdata);
if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount), consensusBranchId))
fComplete = false;
}
if (fComplete) {
// do nothing... for now
// perhaps store this for later optional JSON output
}
tx = mergedTx;
}
class Secp256k1Init
{
ECCVerifyHandle globalVerifyHandle;
public:
Secp256k1Init() {
ECC_Start();
}
~Secp256k1Init() {
ECC_Stop();
}
};
static void MutateTx(CMutableTransaction& tx, const std::string& command,
const std::string& commandVal)
{
boost::scoped_ptr<Secp256k1Init> ecc;
if (command == "nversion")
MutateTxVersion(tx, commandVal);
else if (command == "locktime")
MutateTxLocktime(tx, commandVal);
else if (command == "expiry")
MutateTxExpiry(tx, commandVal);
else if (command == "delin")
MutateTxDelInput(tx, commandVal);
else if (command == "in")
MutateTxAddInput(tx, commandVal);
else if (command == "delout")
MutateTxDelOutput(tx, commandVal);
else if (command == "outaddr")
MutateTxAddOutAddr(tx, commandVal);
else if (command == "outscript")
MutateTxAddOutScript(tx, commandVal);
else if (command == "sign") {
if (!ecc) { ecc.reset(new Secp256k1Init()); }
MutateTxSign(tx, commandVal);
}
else if (command == "load")
RegisterLoad(commandVal);
else if (command == "set")
RegisterSet(commandVal);
else
throw std::runtime_error("unknown command");
}
static void OutputTxJSON(const CTransaction& tx)
{
UniValue entry(UniValue::VOBJ);
TxToUniv(tx, uint256(), entry);
std::string jsonOutput = entry.write(4);
fprintf(stdout, "%s\n", jsonOutput.c_str());
}
static void OutputTxHash(const CTransaction& tx)
{
std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
fprintf(stdout, "%s\n", strHexHash.c_str());
}
static void OutputTxHex(const CTransaction& tx)
{
std::string strHex = EncodeHexTx(tx);
fprintf(stdout, "%s\n", strHex.c_str());
}
static void OutputTx(const CTransaction& tx)
{
if (GetBoolArg("-json", false))
OutputTxJSON(tx);
else if (GetBoolArg("-txid", false))
OutputTxHash(tx);
else
OutputTxHex(tx);
}
static std::string readStdin()
{
char buf[4096];
std::string ret;
while (!feof(stdin)) {
size_t bread = fread(buf, 1, sizeof(buf), stdin);
ret.append(buf, bread);
if (bread < sizeof(buf))
break;
}
if (ferror(stdin))
throw std::runtime_error("error reading stdin");
boost::algorithm::trim_right(ret);
return ret;
}
static int CommandLineRawTx(int argc, char* argv[])
{
std::string strPrint;
int nRet = 0;
try {
// Skip switches; Permit common stdin convention "-"
while (argc > 1 && IsSwitchChar(argv[1][0]) &&
(argv[1][1] != 0)) {
argc--;
argv++;
}
CTransaction txDecodeTmp;
int startArg;
if (!fCreateBlank) {
// require at least one param
if (argc < 2)
throw std::runtime_error("too few parameters");
// param: hex-encoded bitcoin transaction
std::string strHexTx(argv[1]);
if (strHexTx == "-") // "-" implies standard input
strHexTx = readStdin();
if (!DecodeHexTx(txDecodeTmp, strHexTx))
throw std::runtime_error("invalid transaction encoding");
startArg = 2;
} else
startArg = 1;
CMutableTransaction tx(txDecodeTmp);
for (int i = startArg; i < argc; i++) {
std::string arg = argv[i];
std::string key, value;
size_t eqpos = arg.find('=');
if (eqpos == std::string::npos)
key = arg;
else {
key = arg.substr(0, eqpos);
value = arg.substr(eqpos + 1);
}
MutateTx(tx, key, value);
}
OutputTx(tx);
}
catch (const boost::thread_interrupted&) {
throw;
}
catch (const std::exception& e) {
strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
}
catch (...) {
PrintExceptionContinue(NULL, "CommandLineRawTx()");
throw;
}
if (strPrint != "") {
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
}
return nRet;
}
int main(int argc, char* argv[])
{
SetupEnvironment();
try {
int ret = AppInitRawTx(argc, argv);
if (ret != CONTINUE_EXECUTION)
return ret;
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInitRawTx()");
return EXIT_FAILURE;
} catch (...) {
PrintExceptionContinue(NULL, "AppInitRawTx()");
return EXIT_FAILURE;
}
int ret = EXIT_FAILURE;
try {
ret = CommandLineRawTx(argc, argv);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "CommandLineRawTx()");
} catch (...) {
PrintExceptionContinue(NULL, "CommandLineRawTx()");
}
return ret;
}
+170
View File
@@ -0,0 +1,170 @@
// Offline NRB key generation and transparent transaction signing.
// Build with the same flags and libraries as nerbus-tx. No RPC or node startup.
#define main NerbusLegacyTransactionMain
#include "nerbus-original-tx.inc"
#undef main
#include <sodium.h>
#include <set>
static const int64_t WALLET_MAX_MONEY = 209000000LL * COIN;
static CAmount WalletAmount(const UniValue &v)
{
if (!v.isStr()) throw std::runtime_error("Amount must be a decimal string");
CAmount n;
if (!ParseMoney(v.get_str(), n) || n < 0 || n > WALLET_MAX_MONEY)
throw std::runtime_error("Invalid NRB amount");
return n;
}
static UniValue WalletKey(const CKey &key)
{
if (!key.IsValid() || !key.IsCompressed())
throw std::runtime_error("A compressed NRB private key is required");
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(key.GetPubKey().GetID()));
result.pushKV("privateKey", EncodeSecret(key));
return result;
}
static UniValue WalletPayment(const UniValue &request)
{
const auto &inputs = request["inputs"];
const auto &keys = request["privateKeys"];
if (!inputs.isArray() || inputs.empty() || inputs.size() > 100 ||
!keys.isArray() || keys.empty() || keys.size() > 1000)
throw std::runtime_error("Invalid payment inputs");
int height = request["height"].get_int();
if (height < 1 || height > 400000000)
throw std::runtime_error("Invalid chain height");
CAmount amount = WalletAmount(request["amount"]);
CAmount fee = WalletAmount(request["fee"]);
if (amount <= 0 || fee < 10000 || fee > COIN)
throw std::runtime_error("Invalid payment amount or fee");
CTxDestination to = DecodeDestination(request["to"].get_str());
CTxDestination change = DecodeDestination(request["change"].get_str());
if (!boost::get<CKeyID>(&to) || !boost::get<CKeyID>(&change))
throw std::runtime_error("A transparent NRB address is required");
CBasicKeyStore keystore;
for (size_t i = 0; i < keys.size(); ++i) {
CKey key = DecodeSecret(keys[i].get_str());
if (!key.IsValid() || !key.IsCompressed())
throw std::runtime_error("Invalid NRB private key");
keystore.AddKey(key);
}
if (!keystore.HaveKey(*boost::get<CKeyID>(&change)))
throw std::runtime_error("Change address is not owned by this wallet");
CMutableTransaction tx;
tx.fOverwintered = true;
tx.nVersion = SAPLING_TX_VERSION;
tx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
tx.nExpiryHeight = height + 40;
tx.nLockTime = 0;
CAmount total = 0;
std::vector<CTxOut> previous;
std::set<std::pair<uint256, unsigned int>> seen;
for (size_t i = 0; i < inputs.size(); ++i) {
const auto &in = inputs[i];
std::string raw = in["raw"].get_str();
if (raw.size() > 4000000) throw std::runtime_error("Previous transaction is too large");
CTransaction prev;
if (!DecodeHexTx(prev, raw)) throw std::runtime_error("Invalid previous transaction");
uint256 txid = ParseHashUV(in["txid"], "txid");
int index = in["vout"].get_int();
if (prev.GetHash() != txid || index < 0 || (size_t)index >= prev.vout.size())
throw std::runtime_error("Previous transaction does not match the selected input");
if (!seen.emplace(txid, (unsigned int)index).second)
throw std::runtime_error("Duplicate payment input");
const CTxOut &out = prev.vout[index];
CTxDestination dest;
if (!ExtractDestination(out.scriptPubKey, dest) || !boost::get<CKeyID>(&dest) ||
out.scriptPubKey != GetScriptForDestination(dest) ||
!keystore.HaveKey(*boost::get<CKeyID>(&dest)))
throw std::runtime_error("Input is not a standard address owned by this wallet");
if (out.nValue != WalletAmount(in["amount"]) || out.nValue <= 0 ||
total > WALLET_MAX_MONEY - out.nValue)
throw std::runtime_error("Previous output amount does not match");
total += out.nValue;
previous.push_back(out);
tx.vin.emplace_back(txid, (unsigned int)index);
}
if (total < amount + fee) throw std::runtime_error("Insufficient funds");
CAmount changeAmount = total - amount - fee;
if (changeAmount != 0 && changeAmount < 1000)
throw std::runtime_error("Change is too small; adjust the amount or fee");
tx.vout.emplace_back(amount, GetScriptForDestination(to));
if (changeAmount) tx.vout.emplace_back(changeAmount, GetScriptForDestination(change));
const auto branch = CurrentEpochBranchId(height, Params().GetConsensus());
for (size_t i = 0; i < previous.size(); ++i) {
SignatureData sig;
const auto &prev = previous[i];
if (!ProduceSignature(MutableTransactionSignatureCreator(&keystore, &tx, i,
prev.nValue, prev.scriptPubKey, height, SIGHASH_ALL),
prev.scriptPubKey, sig, branch))
throw std::runtime_error("Unable to sign payment input");
UpdateTransaction(tx, i, sig);
if (!VerifyScript(tx.vin[i].scriptSig, prev.scriptPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
MutableTransactionSignatureChecker(&tx, i, prev.nValue), branch))
throw std::runtime_error("Payment signature verification failed");
}
CTransaction completed(tx);
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(completed));
result.pushKV("txid", completed.GetHash().GetHex());
result.pushKV("fee", FormatMoney(fee));
result.pushKV("amount", FormatMoney(amount));
result.pushKV("change", FormatMoney(changeAmount));
return result;
}
int main(int argc, char *argv[])
{
// The only supported interface is a single bounded JSON request on stdin.
// Private keys never appear in command-line arguments or temporary files.
if (argc != 2 || std::string(argv[1]) != "-wallet-json") return EXIT_FAILURE;
try {
SetupEnvironment();
ParseParameters(argc, argv);
SelectParams(CBaseChainParams::MAIN);
if (sodium_init() < 0) throw std::runtime_error("Secure randomness is unavailable");
Secp256k1Init ellipticCurve;
std::string raw;
char buffer[4096];
while (!feof(stdin)) {
size_t n = fread(buffer, 1, sizeof(buffer), stdin);
if (raw.size() + n > 16 * 1024 * 1024)
throw std::runtime_error("Request is too large");
raw.append(buffer, n);
if (n < sizeof(buffer)) break;
}
if (ferror(stdin)) throw std::runtime_error("Unable to read request");
UniValue request;
if (!request.read(raw) || !request.isObject())
throw std::runtime_error("Invalid request");
sodium_memzero(&raw[0], raw.size());
sodium_memzero(buffer, sizeof(buffer));
UniValue result;
std::string action = request["action"].get_str();
if (action == "newKey") {
CKey key;
key.MakeNewKey(true);
result = WalletKey(key);
} else if (action == "importKey") {
result = WalletKey(DecodeSecret(request["privateKey"].get_str()));
} else if (action == "payment") {
result = WalletPayment(request);
} else throw std::runtime_error("Unknown wallet operation");
std::string response = result.write();
fwrite(response.data(), 1, response.size(), stdout);
fputc('\n', stdout);
fflush(stdout);
sodium_memzero(&response[0], response.size());
return EXIT_SUCCESS;
} catch (const std::exception &e) {
UniValue error(UniValue::VOBJ);
error.pushKV("error", e.what());
fprintf(stdout, "%s\n", error.write().c_str());
return EXIT_FAILURE;
}
}