diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 54bec862..a189b2b2 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -44,6 +44,8 @@ static CCriticalSection cs_nWalletUnlockTime; extern Value dumpprivkey(const Array& params, bool fHelp); extern Value importprivkey(const Array& params, bool fHelp); +const Object emptyobj; + Object JSONRPCError(int code, const string& message) { Object error; @@ -111,6 +113,33 @@ HexBits(unsigned int nBits) return HexStr(BEGIN(uBits.cBits), END(uBits.cBits)); } +enum DecomposeMode { + DM_NONE = 0, + DM_HASH, + DM_HEX, + DM_ASM, + DM_OBJ, +}; + +enum DecomposeMode +FindDecompose(const Object& decompositions, const char* pcType, const char* pcDefault) +{ + Value val = find_value(decompositions, pcType); + std::string strDecompose = (val.type() == null_type) ? pcDefault : val.get_str(); + + if (strDecompose == "no") + return DM_NONE; + if (strDecompose == "hash") + return DM_HASH; + if (strDecompose == "hex") + return DM_HEX; + if (strDecompose == "asm") + return DM_ASM; + if (strDecompose == "obj") + return DM_OBJ; + throw JSONRPCError(-18, "Invalid decomposition"); +} + void WalletTxToJSON(const CWalletTx& wtx, Object& entry) { int confirms = wtx.GetDepthInMainChain(); @@ -126,11 +155,74 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) entry.push_back(Pair(item.first, item.second)); } -void TxToJSON(const CTransaction &tx, Object& entry) +void +ScriptSigToJSON(const CTxIn& txin, Object& out) +{ + out.push_back(Pair("asm", txin.scriptSig.ToString())); + out.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + + CTransaction txprev; + uint256 hashTxprevBlock; + if (!GetTransaction(txin.prevout.hash, txprev, hashTxprevBlock)) + return; + + txnouttype type; + vector addresses; + int nRequired; + + if (!ExtractAddresses(txprev.vout[txin.prevout.n].scriptPubKey, type, + addresses, nRequired)) + { + out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD))); + return; + } + + out.push_back(Pair("type", GetTxnOutputType(type))); + if (type == TX_MULTISIG) + { + // TODO: Need to handle this specially since not all input addresses are required... + return; + } + + Array a; + BOOST_FOREACH(const CBitcoinAddress& addr, addresses) + a.push_back(addr.ToString()); + out.push_back(Pair("addresses", a)); +} + +void +ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out) +{ + txnouttype type; + vector addresses; + int nRequired; + + out.push_back(Pair("asm", scriptPubKey.ToString())); + out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); + + if (!ExtractAddresses(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 CBitcoinAddress& addr, addresses) + a.push_back(addr.ToString()); + out.push_back(Pair("addresses", a)); +} + +void TxToJSON(const CTransaction &tx, Object& entry, const Object& decompositions) { entry.push_back(Pair("version", tx.nVersion)); entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime)); entry.push_back(Pair("size", (boost::int64_t)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); + + enum DecomposeMode decomposeScript = FindDecompose(decompositions, "script", "asm"); + Array vin; BOOST_FOREACH(const CTxIn& txin, tx.vin) { @@ -143,7 +235,25 @@ void TxToJSON(const CTransaction &tx, Object& entry) prevout.push_back(Pair("hash", txin.prevout.hash.GetHex())); prevout.push_back(Pair("n", (boost::int64_t)txin.prevout.n)); in.push_back(Pair("prevout", prevout)); - in.push_back(Pair("scriptSig", txin.scriptSig.ToString())); + switch (decomposeScript) { + case DM_NONE: + break; + case DM_HEX: + in.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + break; + case DM_ASM: + in.push_back(Pair("scriptSig", txin.scriptSig.ToString())); + break; + case DM_OBJ: + { + Object o; + ScriptSigToJSON(txin, o); + in.push_back(Pair("scriptSig", o)); + break; + } + default: + throw JSONRPCError(-18, "Invalid script decomposition"); + } } in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence)); vin.push_back(in); @@ -154,12 +264,32 @@ void TxToJSON(const CTransaction &tx, Object& entry) { Object out; out.push_back(Pair("value", ValueFromAmount(txout.nValue))); - out.push_back(Pair("scriptPubKey", txout.scriptPubKey.ToString())); + switch (decomposeScript) { + case DM_NONE: + break; + case DM_HEX: + out.push_back(Pair("scriptPubKey", HexStr(txout.scriptPubKey.begin(), txout.scriptPubKey.end()))); + break; + case DM_ASM: + out.push_back(Pair("scriptPubKey", txout.scriptPubKey.ToString())); + break; + case DM_OBJ: + { + Object o; + ScriptPubKeyToJSON(txout.scriptPubKey, o); + out.push_back(Pair("scriptPubKey", o)); + break; + } + default: + throw JSONRPCError(-18, "Invalid script decomposition"); + } vout.push_back(out); } entry.push_back(Pair("vout", vout)); } +void AnyTxToJSON(const uint256 hash, const CTransaction* ptx, Object& entry, const Object& decompositions); + string AccountFromValue(const Value& value) { string strAccount = value.get_str(); @@ -168,10 +298,13 @@ string AccountFromValue(const Value& value) return strAccount; } -Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) +Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, const Object& decompositions) { Object result; result.push_back(Pair("hash", block.GetHash().GetHex())); + CMerkleTx txGen(block.vtx[0]); + txGen.SetMerkleBranch(&block); + result.push_back(Pair("confirmations", (int)txGen.GetDepthInMainChain())); result.push_back(Pair("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION))); result.push_back(Pair("height", blockindex->nHeight)); result.push_back(Pair("version", block.nVersion)); @@ -180,10 +313,38 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) result.push_back(Pair("nonce", (boost::uint64_t)block.nNonce)); result.push_back(Pair("bits", HexBits(block.nBits))); result.push_back(Pair("difficulty", GetDifficulty(blockindex))); - Array txhashes; - BOOST_FOREACH (const CTransaction&tx, block.vtx) - txhashes.push_back(tx.GetHash().GetHex()); - result.push_back(Pair("tx", txhashes)); + + enum DecomposeMode decomposeTxn = FindDecompose(decompositions, "tx", "hash"); + if (decomposeTxn) + { + Array txs; + switch (decomposeTxn) { + case DM_OBJ: + BOOST_FOREACH (const CTransaction&tx, block.vtx) + { + Object entry; + AnyTxToJSON(tx.GetHash(), &tx, entry, decompositions); + txs.push_back(entry); + } + break; + case DM_HEX: + BOOST_FOREACH (const CTransaction&tx, block.vtx) + { + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << tx; + + txs.push_back(HexStr(ssTx.begin(), ssTx.end())); + } + break; + case DM_HASH: + BOOST_FOREACH (const CTransaction&tx, block.vtx) + txs.push_back(tx.GetHash().GetHex()); + break; + default: + throw JSONRPCError(-18, "Invalid transaction decomposition"); + } + result.push_back(Pair("tx", txs)); + } if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); @@ -194,6 +355,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) + /// /// Note: This interface may still be subject to change. /// @@ -1498,23 +1660,14 @@ Value listsinceblock(const Array& params, bool fHelp) return ret; } -Value gettransaction(const Array& params, bool fHelp) +void +AnyTxToJSON(const uint256 hash, const CTransaction* ptx, Object& entry, const Object& decompositions) { - if (fHelp || params.size() != 1) - throw runtime_error( - "gettransaction \n" - "Get detailed information about "); - - uint256 hash; - hash.SetHex(params[0].get_str()); - - Object entry; - if (pwalletMain->mapWallet.count(hash)) { const CWalletTx& wtx = pwalletMain->mapWallet[hash]; - TxToJSON(wtx, entry); + TxToJSON(wtx, entry, decompositions); int64 nCredit = wtx.GetCredit(); int64 nDebit = wtx.GetDebit(); @@ -1535,10 +1688,12 @@ Value gettransaction(const Array& params, bool fHelp) { CTransaction tx; uint256 hashBlock = 0; - if (GetTransaction(hash, tx, hashBlock)) + if ((!ptx) && GetTransaction(hash, tx, hashBlock)) + ptx = &tx; + if (ptx) { entry.push_back(Pair("txid", hash.GetHex())); - TxToJSON(tx, entry); + TxToJSON(*ptx, entry, decompositions); if (hashBlock == 0) entry.push_back(Pair("confirmations", 0)); else @@ -1561,6 +1716,22 @@ Value gettransaction(const Array& params, bool fHelp) else throw JSONRPCError(-5, "No information available about transaction"); } +} + +Value gettransaction(const Array& params, bool fHelp) +{ + if (fHelp || params.size() < 1 || params.size() > 2) + throw runtime_error( + "gettransaction [decompositions]\n" + "Get detailed information about "); + + uint256 hash; + hash.SetHex(params[0].get_str()); + + Object entry; + + AnyTxToJSON(hash, NULL, entry, + (params.size() > 1) ? params[1].get_obj() : emptyobj); return entry; } @@ -2042,9 +2213,9 @@ Value getblockhash(const Array& params, bool fHelp) Value getblock(const Array& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp || params.size() < 1 || params.size() > 2) throw runtime_error( - "getblock \n" + "getblock [decompositions]\n" "Returns details of a block with given block-hash."); std::string strHash = params[0].get_str(); @@ -2057,7 +2228,8 @@ Value getblock(const Array& params, bool fHelp) CBlockIndex* pblockindex = mapBlockIndex[hash]; block.ReadFromDisk(pblockindex, true); - return blockToJSON(block, pblockindex); + return blockToJSON(block, pblockindex, + (params.size() > 1) ? params[1].get_obj() : emptyobj); } @@ -2715,7 +2887,9 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 0) ConvertTo(params[0]); if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "getblock" && n > 1) ConvertTo(params[1]); if (strMethod == "getblockhash" && n > 0) ConvertTo(params[0]); + if (strMethod == "gettransaction" && n > 1) ConvertTo(params[1]); if (strMethod == "move" && n > 2) ConvertTo(params[2]); if (strMethod == "move" && n > 3) ConvertTo(params[3]); if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]);