123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- // ECOin - Copyright (c) - 2014/2022 - GPLv3 - epsylon@riseup.net (https://03c8.net)
- #include <boost/assign/list_of.hpp>
- #include "base58.h"
- #include "ecoinrpc.h"
- #include "txdb.h"
- #include "init.h"
- #include "main.h"
- #include "net.h"
- #include "wallet.h"
- using namespace std;
- using namespace boost;
- using namespace boost::assign;
- using namespace json_spirit;
- void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex)
- {
- txnouttype type;
- vector<CTxDestination> addresses;
- int nRequired;
- out.push_back(Pair("asm", scriptPubKey.ToString()));
- if (fIncludeHex)
- out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
- if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired))
- {
- out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
- return;
- }
- out.push_back(Pair("reqSigs", nRequired));
- out.push_back(Pair("type", GetTxnOutputType(type)));
- Array a;
- BOOST_FOREACH(const CTxDestination& addr, addresses)
- a.push_back(CEcoinAddress(addr).ToString());
- out.push_back(Pair("addresses", a));
- }
- void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
- {
- entry.push_back(Pair("txid", tx.GetHash().GetHex()));
- entry.push_back(Pair("version", tx.nVersion));
- entry.push_back(Pair("time", (boost::int64_t)tx.nTime));
- entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime));
- Array vin;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
- {
- Object in;
- if (tx.IsCoinBase())
- in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
- else
- {
- in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
- in.push_back(Pair("vout", (boost::int64_t)txin.prevout.n));
- Object o;
- o.push_back(Pair("asm", txin.scriptSig.ToString()));
- o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
- in.push_back(Pair("scriptSig", o));
- }
- in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence));
- vin.push_back(in);
- }
- entry.push_back(Pair("vin", vin));
- Array vout;
- for (unsigned int i = 0; i < tx.vout.size(); i++)
- {
- const CTxOut& txout = tx.vout[i];
- Object out;
- out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
- out.push_back(Pair("n", (boost::int64_t)i));
- Object o;
- ScriptPubKeyToJSON(txout.scriptPubKey, o, false);
- out.push_back(Pair("scriptPubKey", o));
- vout.push_back(out);
- }
- entry.push_back(Pair("vout", vout));
- if (hashBlock != 0)
- {
- entry.push_back(Pair("blockhash", hashBlock.GetHex()));
- map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
- if (mi != mapBlockIndex.end() && (*mi).second)
- {
- CBlockIndex* pindex = (*mi).second;
- if (pindex->IsInMainChain())
- {
- entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
- entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
- entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime));
- }
- else
- entry.push_back(Pair("confirmations", 0));
- }
- }
- }
- Value getrawtransaction(const Array& params, bool fHelp)
- {
- if (fHelp || params.size() < 1 || params.size() > 2)
- throw runtime_error(
- "getrawtransaction <txid> [verbose=0]\n"
- "If verbose=0, returns a string that is\n"
- "serialized, hex-encoded data for <txid>.\n"
- "If verbose is non-zero, returns an Object\n"
- "with information about <txid>.");
- uint256 hash;
- hash.SetHex(params[0].get_str());
- bool fVerbose = false;
- if (params.size() > 1)
- fVerbose = (params[1].get_int() != 0);
- CTransaction tx;
- uint256 hashBlock = 0;
- if (!GetTransaction(hash, tx, hashBlock))
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << tx;
- string strHex = HexStr(ssTx.begin(), ssTx.end());
- if (!fVerbose)
- return strHex;
- Object result;
- result.push_back(Pair("hex", strHex));
- TxToJSON(tx, hashBlock, result);
- return result;
- }
- Value listunspent(const Array& params, bool fHelp)
- {
- if (fHelp || params.size() > 3)
- throw runtime_error(
- "listunspent [minconf=1] [maxconf=9999999] [\"address\",...]\n"
- "Returns array of unspent transaction outputs\n"
- "with between minconf and maxconf (inclusive) confirmations.\n"
- "Optionally filtered to only include txouts paid to specified addresses.\n"
- "Results are an array of Objects, each of which has:\n"
- "{txid, vout, scriptPubKey, amount, confirmations}");
- RPCTypeCheck(params, list_of(int_type)(int_type)(array_type));
- int nMinDepth = 1;
- if (params.size() > 0)
- nMinDepth = params[0].get_int();
- int nMaxDepth = 9999999;
- if (params.size() > 1)
- nMaxDepth = params[1].get_int();
- set<CEcoinAddress> setAddress;
- if (params.size() > 2)
- {
- Array inputs = params[2].get_array();
- BOOST_FOREACH(Value& input, inputs)
- {
- CEcoinAddress address(input.get_str());
- if (!address.IsValid())
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Ecoin address: ")+input.get_str());
- if (setAddress.count(address))
- throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str());
- setAddress.insert(address);
- }
- }
- Array results;
- vector<COutput> vecOutputs;
- pwalletMain->AvailableCoins(vecOutputs, false);
- BOOST_FOREACH(const COutput& out, vecOutputs)
- {
- if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
- continue;
- if(setAddress.size())
- {
- CTxDestination address;
- if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
- continue;
- if (!setAddress.count(address))
- continue;
- }
- int64 nValue = out.tx->vout[out.i].nValue;
- const CScript& pk = out.tx->vout[out.i].scriptPubKey;
- Object entry;
- entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
- entry.push_back(Pair("vout", out.i));
- CTxDestination address;
- if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
- {
- entry.push_back(Pair("address", CEcoinAddress(address).ToString()));
- if (pwalletMain->mapAddressBook.count(address))
- entry.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
- }
- entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
- entry.push_back(Pair("amount",ValueFromAmount(nValue)));
- entry.push_back(Pair("confirmations",out.nDepth));
- results.push_back(entry);
- }
- return results;
- }
- Value createrawtransaction(const Array& params, bool fHelp)
- {
- if (fHelp || params.size() != 2)
- throw runtime_error(
- "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n"
- "Create a transaction spending given inputs\n"
- "(array of objects containing transaction id and output number),\n"
- "sending to given address(es).\n"
- "Returns hex-encoded raw transaction.\n"
- "Note that the transaction's inputs are not signed, and\n"
- "it is not stored in the wallet or transmitted to the network.");
- RPCTypeCheck(params, list_of(array_type)(obj_type));
- Array inputs = params[0].get_array();
- Object sendTo = params[1].get_obj();
- CTransaction rawTx;
- BOOST_FOREACH(Value& input, inputs)
- {
- const Object& o = input.get_obj();
- const Value& txid_v = find_value(o, "txid");
- if (txid_v.type() != str_type)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key");
- string txid = txid_v.get_str();
- if (!IsHex(txid))
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
- const Value& vout_v = find_value(o, "vout");
- if (vout_v.type() != int_type)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
- int nOutput = vout_v.get_int();
- if (nOutput < 0)
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
- CTxIn in(COutPoint(uint256(txid), nOutput));
- rawTx.vin.push_back(in);
- }
- set<CEcoinAddress> setAddress;
- BOOST_FOREACH(const Pair& s, sendTo)
- {
- CEcoinAddress address(s.name_);
- if (!address.IsValid())
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Ecoin address: ")+s.name_);
- if (setAddress.count(address))
- throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
- setAddress.insert(address);
- CScript scriptPubKey;
- scriptPubKey.SetDestination(address.Get());
- int64 nAmount = AmountFromValue(s.value_);
- CTxOut out(nAmount, scriptPubKey);
- rawTx.vout.push_back(out);
- }
- CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
- ss << rawTx;
- return HexStr(ss.begin(), ss.end());
- }
- Value decoderawtransaction(const Array& params, bool fHelp)
- {
- if (fHelp || params.size() != 1)
- throw runtime_error(
- "decoderawtransaction <hex string>\n"
- "Return a JSON object representing the serialized, hex-encoded transaction.");
- RPCTypeCheck(params, list_of(str_type));
- vector<unsigned char> txData(ParseHex(params[0].get_str()));
- CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
- CTransaction tx;
- try {
- ssData >> tx;
- }
- catch (std::exception &e) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
- Object result;
- TxToJSON(tx, 0, result);
- return result;
- }
- Value decodescript(const Array& params, bool fHelp)
- {
- if (fHelp || params.size() != 1)
- throw runtime_error(
- "decodescript <hex string>\n"
- "Decode a hex-encoded script.");
- // RPCTypeCheck(params, list_of(str_type));
- // Object r;
- // CScript script;
- // if (params[0].get_str().size() > 0){
- // vector<unsigned char> scriptData(ParseHex(params[0], "argument"));
- // script = CScript(scriptData.begin(), scriptData.end());
- // } else {
- // // Empty scripts are valid
- // }
- // ScriptPubKeyToJSON(script, r, false);
- // r.push_back(Pair("p2sh", CEcoinAddress(script.GetID()).ToString()));
- // return r;
- }
- Value signrawtransaction(const Array& params, bool fHelp)
- {
- if (fHelp || params.size() < 1 || params.size() > 4)
- throw runtime_error(
- "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
- "Sign inputs for raw transaction (serialized, hex-encoded).\n"
- "Second optional argument (may be null) is an array of previous transaction outputs that\n"
- "this transaction depends on but may not yet be in the blockchain.\n"
- "Third optional argument (may be null) is an array of base58-encoded private\n"
- "keys that, if given, will be the only keys used to sign the transaction.\n"
- "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n"
- "ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n"
- "Returns json object with keys:\n"
- " hex : raw transaction with signature(s) (hex-encoded string)\n"
- " complete : 1 if transaction has a complete set of signature (0 if not)"
- + HelpRequiringPassphrase());
- RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true);
- vector<unsigned char> txData(ParseHex(params[0].get_str()));
- CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
- vector<CTransaction> txVariants;
- while (!ssData.empty())
- {
- try {
- CTransaction tx;
- ssData >> tx;
- txVariants.push_back(tx);
- }
- catch (std::exception &e) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
- }
- if (txVariants.empty())
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
- // mergedTx will end up with all the signatures; it
- // starts as a clone of the rawtx:
- CTransaction mergedTx(txVariants[0]);
- bool fComplete = true;
- // Fetch previous transactions (inputs):
- map<COutPoint, CScript> mapPrevOut;
- for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
- {
- CTransaction tempTx;
- MapPrevTx mapPrevTx;
- CTxDB txdb("r");
- map<uint256, CTxIndex> unused;
- bool fInvalid;
- // FetchInputs aborts on failure, so we go one at a time.
- tempTx.vin.push_back(mergedTx.vin[i]);
- tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
- // Copy results into mapPrevOut:
- BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
- {
- const uint256& prevHash = txin.prevout.hash;
- if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n)
- mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
- }
- }
- // Add previous txouts given in the RPC call:
- if (params.size() > 1 && params[1].type() != null_type)
- {
- Array prevTxs = params[1].get_array();
- BOOST_FOREACH(Value& p, prevTxs)
- {
- if (p.type() != obj_type)
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
- Object prevOut = p.get_obj();
- RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
- string txidHex = find_value(prevOut, "txid").get_str();
- if (!IsHex(txidHex))
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal");
- uint256 txid;
- txid.SetHex(txidHex);
- int nOut = find_value(prevOut, "vout").get_int();
- if (nOut < 0)
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
- string pkHex = find_value(prevOut, "scriptPubKey").get_str();
- if (!IsHex(pkHex))
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal");
- vector<unsigned char> pkData(ParseHex(pkHex));
- CScript scriptPubKey(pkData.begin(), pkData.end());
- COutPoint outpoint(txid, nOut);
- if (mapPrevOut.count(outpoint))
- {
- // Complain if scriptPubKey doesn't match
- if (mapPrevOut[outpoint] != scriptPubKey)
- {
- string err("Previous output scriptPubKey mismatch:\n");
- err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+
- scriptPubKey.ToString();
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
- }
- }
- else
- mapPrevOut[outpoint] = scriptPubKey;
- }
- }
- bool fGivenKeys = false;
- CBasicKeyStore tempKeystore;
- if (params.size() > 2 && params[2].type() != null_type)
- {
- fGivenKeys = true;
- Array keys = params[2].get_array();
- BOOST_FOREACH(Value k, keys)
- {
- CEcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(k.get_str());
- if (!fGood)
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,"Invalid private key");
- CKey key;
- bool fCompressed;
- CSecret secret = vchSecret.GetSecret(fCompressed);
- key.SetSecret(secret, fCompressed);
- tempKeystore.AddKey(key);
- }
- }
- else
- EnsureWalletIsUnlocked();
- const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
- int nHashType = SIGHASH_ALL;
- if (params.size() > 3 && params[3].type() != null_type)
- {
- static map<string, int> mapSigHashValues =
- boost::assign::map_list_of
- (string("ALL"), int(SIGHASH_ALL))
- (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
- (string("NONE"), int(SIGHASH_NONE))
- (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
- (string("SINGLE"), int(SIGHASH_SINGLE))
- (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
- ;
- string strHashType = params[3].get_str();
- if (mapSigHashValues.count(strHashType))
- nHashType = mapSigHashValues[strHashType];
- else
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
- }
- bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
- // Sign what we can:
- for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
- {
- CTxIn& txin = mergedTx.vin[i];
- if (mapPrevOut.count(txin.prevout) == 0)
- {
- fComplete = false;
- continue;
- }
- const CScript& prevPubKey = mapPrevOut[txin.prevout];
- txin.scriptSig.clear();
- // Only sign SIGHASH_SINGLE if there's a corresponding output:
- if (!fHashSingle || (i < mergedTx.vout.size()))
- SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
- // ... and merge in other signatures:
- BOOST_FOREACH(const CTransaction& txv, txVariants)
- {
- txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
- }
- if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
- fComplete = false;
- }
- Object result;
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << mergedTx;
- result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
- result.push_back(Pair("complete", fComplete));
- return result;
- }
- Value sendrawtransaction(const Array& params, bool fHelp)
- {
- if (fHelp || params.size() < 1 || params.size() > 1)
- throw runtime_error(
- "sendrawtransaction <hex string>\n"
- "Submits raw transaction (serialized, hex-encoded) to local node and network.");
- RPCTypeCheck(params, list_of(str_type));
- // parse hex string from parameter
- vector<unsigned char> txData(ParseHex(params[0].get_str()));
- CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
- CTransaction tx;
- // deserialize binary data stream
- try {
- ssData >> tx;
- }
- catch (std::exception &e) {
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
- }
- uint256 hashTx = tx.GetHash();
- // See if the transaction is already in a block or in the memory pool
- CTransaction existingTx;
- uint256 hashBlock = 0;
- if (GetTransaction(hashTx, existingTx, hashBlock))
- {
- if (hashBlock != 0)
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("transaction already in block ")+hashBlock.GetHex());
- // Not in block, but already in the memory pool; will drop through to re-relay it
- }
- else
- {
- // push to local node
- CTxDB txdb("r");
- if (!tx.AcceptToMemoryPool(txdb))
- throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
- SyncWithWallets(tx, NULL, true);
- }
- RelayTransaction(tx, hashTx);
- return hashTx.GetHex();
- }
|