changeset 2416:85fc588fb80c draft

Merge pull request #886 from luke-jr/getblock_full More details for getblock and gettransaction
author Gregory Maxwell <gmaxwell@gmail.com>
date Tue, 08 May 2012 15:31:52 -0700
parents 1355c3a53b7f (current diff) b7e51b5cdc07 (diff)
children ba6ae7e6cd33
files
diffstat 1 files changed, 199 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -44,6 +44,8 @@
 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 @@
     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 @@
         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<CBitcoinAddress> 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<CBitcoinAddress> 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 @@
             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 @@
     {
         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 @@
     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 @@
     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 @@
 
 
 
+
 ///
 /// Note: This interface may still be subject to change.
 ///
@@ -1498,23 +1660,14 @@
     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 <txid>\n"
-            "Get detailed information about <txid>");
-
-    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 @@
     {
         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 @@
         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 <txid> [decompositions]\n"
+            "Get detailed information about <txid>");
+
+    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 getblock(const Array& params, bool fHelp)
 {
-    if (fHelp || params.size() != 1)
+    if (fHelp || params.size() < 1 || params.size() > 2)
         throw runtime_error(
-            "getblock <hash>\n"
+            "getblock <hash> [decompositions]\n"
             "Returns details of a block with given block-hash.");
 
     std::string strHash = params[0].get_str();
@@ -2057,7 +2228,8 @@
     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 @@
     if (strMethod == "listreceivedbyaccount"  && n > 0) ConvertTo<boost::int64_t>(params[0]);
     if (strMethod == "listreceivedbyaccount"  && n > 1) ConvertTo<bool>(params[1]);
     if (strMethod == "getbalance"             && n > 1) ConvertTo<boost::int64_t>(params[1]);
+    if (strMethod == "getblock"               && n > 1) ConvertTo<Object>(params[1]);
     if (strMethod == "getblockhash"           && n > 0) ConvertTo<boost::int64_t>(params[0]);
+    if (strMethod == "gettransaction"         && n > 1) ConvertTo<Object>(params[1]);
     if (strMethod == "move"                   && n > 2) ConvertTo<double>(params[2]);
     if (strMethod == "move"                   && n > 3) ConvertTo<boost::int64_t>(params[3]);
     if (strMethod == "sendfrom"               && n > 2) ConvertTo<double>(params[2]);