From d4e09300f3f9deb401e38fdf50fd0ff8d3f15b50 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 22 Feb 2012 13:26:25 -0500 Subject: [PATCH 1/3] Add block "confirmations" to getblock, mainly for identifying orphans --- src/bitcoinrpc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 54bec8623..7d2450c16 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -172,6 +172,9 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) { 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)); From 74335bd32a7bef564d536ba9cf1be64714ceb8fa Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 22 Feb 2012 17:44:09 -0500 Subject: [PATCH 2/3] Second parameter to JSON-RPC getblock/gettransaction: decompositions This is an Object specifying how to decompose specific elements. Currently supported: - "tx": "no", "hash", "hex", "obj" - "script": "no", "hex", "asm" --- src/bitcoinrpc.cpp | 147 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 122 insertions(+), 25 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 7d2450c16..34c43a9e3 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,14 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) entry.push_back(Pair(item.first, item.second)); } -void TxToJSON(const CTransaction &tx, Object& entry) +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 +175,18 @@ 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; + default: + throw JSONRPCError(-18, "Invalid script decomposition"); + } } in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence)); vin.push_back(in); @@ -154,12 +197,25 @@ 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; + 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,7 +224,7 @@ 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())); @@ -183,10 +239,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())); @@ -197,6 +281,7 @@ Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex) + /// /// Note: This interface may still be subject to change. /// @@ -1501,23 +1586,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(); @@ -1538,10 +1614,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 @@ -1564,6 +1642,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; } @@ -2045,9 +2139,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(); @@ -2060,7 +2154,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); } @@ -2718,7 +2813,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]); From 7e63dc361512ccaeba5dd31e90549e2fe4d0c1a3 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 22 Feb 2012 17:44:46 -0500 Subject: [PATCH 3/3] Support for decomposing scripts as "obj" --- src/bitcoinrpc.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 34c43a9e3..a189b2b2b 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -155,6 +155,66 @@ void WalletTxToJSON(const CWalletTx& wtx, Object& entry) entry.push_back(Pair(item.first, item.second)); } +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)); @@ -184,6 +244,13 @@ void TxToJSON(const CTransaction &tx, Object& entry, const Object& decomposition 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"); } @@ -206,6 +273,13 @@ void TxToJSON(const CTransaction &tx, Object& entry, const Object& decomposition 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"); }