diff options
Diffstat (limited to 'src/rpcrawtransaction.cpp')
-rw-r--r-- | src/rpcrawtransaction.cpp | 231 |
1 files changed, 132 insertions, 99 deletions
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index e634ed7ddc..9531b12678 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -18,6 +18,39 @@ using namespace boost; using namespace boost::assign; using namespace json_spirit; +// +// Utilities: convert hex-encoded Values +// (throws error if not hex). +// +uint256 ParseHashV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) // Note: IsHex("") is false + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + uint256 result; + result.SetHex(strHex); + return result; +} +uint256 ParseHashO(const Object& o, string strKey) +{ + return ParseHashV(find_value(o, strKey), strKey); +} +vector<unsigned char> ParseHexV(const Value& v, string strName) +{ + string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + return ParseHex(strHex); +} +vector<unsigned char> ParseHexO(const Object& o, string strKey) +{ + return ParseHexV(find_value(o, strKey), strKey); +} + void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out) { txnouttype type; @@ -109,8 +142,7 @@ Value getrawtransaction(const Array& params, bool fHelp) "If verbose is non-zero, returns an Object\n" "with information about <txid>."); - uint256 hash; - hash.SetHex(params[0].get_str()); + uint256 hash = ParseHashV(params[0], "parameter 1"); bool fVerbose = false; if (params.size() > 1) @@ -118,7 +150,7 @@ Value getrawtransaction(const Array& params, bool fHelp) CTransaction tx; uint256 hashBlock = 0; - if (!GetTransaction(hash, tx, hashBlock)) + if (!GetTransaction(hash, tx, hashBlock, true)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); @@ -178,10 +210,10 @@ Value listunspent(const Array& params, bool fHelp) if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; - if(setAddress.size()) + if (setAddress.size()) { CTxDestination address; - if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) + if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) continue; if (!setAddress.count(address)) @@ -194,6 +226,17 @@ Value listunspent(const Array& params, bool fHelp) entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); entry.push_back(Pair("vout", out.i)); entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); + if (pk.IsPayToScriptHash()) + { + CTxDestination address; + if (ExtractDestination(pk, address)) + { + const CScriptID& hash = boost::get<const CScriptID&>(address); + CScript redeemScript; + if (pwalletMain->GetCScript(hash, redeemScript)) + entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); + } + } entry.push_back(Pair("amount",ValueFromAmount(nValue))); entry.push_back(Pair("confirmations",out.nDepth)); results.push_back(entry); @@ -221,16 +264,11 @@ Value createrawtransaction(const Array& params, bool fHelp) CTransaction rawTx; - BOOST_FOREACH(Value& input, inputs) + BOOST_FOREACH(const 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"); + uint256 txid = ParseHashO(o, "txid"); const Value& vout_v = find_value(o, "vout"); if (vout_v.type() != int_type) @@ -239,7 +277,7 @@ Value createrawtransaction(const Array& params, bool fHelp) if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); - CTxIn in(COutPoint(uint256(txid), nOutput)); + CTxIn in(COutPoint(txid, nOutput)); rawTx.vin.push_back(in); } @@ -274,9 +312,7 @@ Value decoderawtransaction(const Array& params, bool fHelp) "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())); + vector<unsigned char> txData(ParseHexV(params[0], "argument")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; try { @@ -296,10 +332,10 @@ 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" + "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":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" + "this transaction depends on but may not yet be in the block chain.\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" @@ -311,7 +347,7 @@ Value signrawtransaction(const Array& params, bool fHelp) RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true); - vector<unsigned char> txData(ParseHex(params[0].get_str())); + vector<unsigned char> txData(ParseHexV(params[0], "argument 1")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); vector<CTransaction> txVariants; while (!ssData.empty()) @@ -335,27 +371,44 @@ Value signrawtransaction(const Array& params, bool fHelp) bool fComplete = true; // Fetch previous transactions (inputs): - map<COutPoint, CScript> mapPrevOut; - for (unsigned int i = 0; i < mergedTx.vin.size(); i++) + CCoinsView viewDummy; + CCoinsViewCache view(viewDummy); { - 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) - { + LOCK(mempool.cs); + CCoinsViewCache &viewChain = *pcoinsTip; + CCoinsViewMemPool viewMempool(viewChain, mempool); + view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view + + BOOST_FOREACH(const CTxIn& txin, mergedTx.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; + CCoins coins; + view.GetCoins(prevHash, coins); // this is certainly allowed to fail + } + + view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long + } + + 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) + { + CBitcoinSecret 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(); // Add previous txouts given in the RPC call: if (params.size() > 1 && params[1].type() != null_type) @@ -368,62 +421,44 @@ Value signrawtransaction(const Array& params, bool fHelp) Object prevOut = p.get_obj(); - RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); + RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",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); + uint256 txid = ParseHashO(prevOut, "txid"); 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)); + vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); - COutPoint outpoint(txid, nOut); - if (mapPrevOut.count(outpoint)) - { - // Complain if scriptPubKey doesn't match - if (mapPrevOut[outpoint] != scriptPubKey) - { + CCoins coins; + if (view.GetCoins(txid, coins)) { + if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) { string err("Previous output scriptPubKey mismatch:\n"); - err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+ + err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ scriptPubKey.ToString(); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); } + // what todo if txid is known, but the actual output isn't? + } + if ((unsigned int)nOut >= coins.vout.size()) + coins.vout.resize(nOut+1); + coins.vout[nOut].scriptPubKey = scriptPubKey; + coins.vout[nOut].nValue = 0; // we don't know the actual output value + view.SetCoins(txid, coins); + + // if redeemScript given and not using the local wallet (private keys + // given), add redeemScript to the tempKeystore so it can be signed: + Value v = find_value(prevOut, "redeemScript"); + if (fGivenKeys && scriptPubKey.IsPayToScriptHash() && !(v == Value::null)) + { + vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); + CScript redeemScript(rsData.begin(), rsData.end()); + tempKeystore.AddCScript(redeemScript); } - 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) - { - CBitcoinSecret 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); @@ -452,12 +487,13 @@ Value signrawtransaction(const Array& params, bool fHelp) for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { CTxIn& txin = mergedTx.vin[i]; - if (mapPrevOut.count(txin.prevout) == 0) + CCoins coins; + if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n)) { fComplete = false; continue; } - const CScript& prevPubKey = mapPrevOut[txin.prevout]; + const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey; txin.scriptSig.clear(); // Only sign SIGHASH_SINGLE if there's a corresponding output: @@ -469,7 +505,7 @@ Value signrawtransaction(const Array& params, bool fHelp) { txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig); } - if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0)) + if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0)) fComplete = false; } @@ -489,10 +525,8 @@ Value sendrawtransaction(const Array& params, bool fHelp) "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())); + vector<unsigned char> txData(ParseHexV(params[0], "parameter")); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CTransaction tx; @@ -505,25 +539,24 @@ Value sendrawtransaction(const Array& params, bool fHelp) } 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)) + bool fHave = false; + CCoinsViewCache &view = *pcoinsTip; + CCoins existingCoins; { - if (hashBlock != 0) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("transaction already in block ")+hashBlock.GetHex()); + fHave = view.GetCoins(hashTx, existingCoins); + if (!fHave) { + // push to local node + if (!tx.AcceptToMemoryPool()) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); + } + } + if (fHave) { + if (existingCoins.nHeight < 1000000000) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "transaction already in block chain"); // 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); + } else { + SyncWithWallets(hashTx, tx, NULL, true); } RelayMessage(CInv(MSG_TX, hashTx), tx); |