diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -753,8 +753,8 @@ "key\n" " \"redeemScript\": \"hex\", (string, required for P2SH " "or P2WSH) redeem script\n" - " \"amount\": value (numeric, required) The " - "amount spent\n" + " \"amount\": \"value\" (string, required) The " + "decimal amount spent\n" " }\n" " ,...\n" " ]\n" @@ -906,6 +906,7 @@ {"txid", UniValueType(UniValue::VSTR)}, {"vout", UniValueType(UniValue::VNUM)}, {"scriptPubKey", UniValueType(UniValue::VSTR)}, + {"amount", UniValueType(UniValue::VSTR)}, }); uint256 txid = ParseHashO(prevOut, "txid"); @@ -940,6 +941,13 @@ coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount")); } + if (coins->vout[nOut].nValue <= 0LL) { + // 'amount' param is now required to be >0 in signed tx + throw JSONRPCError(RPC_INVALID_PARAMETER, + strprintf("Amount on previous output " + "%d must be >0", + nOut)); + } } // If redeemScript given and not using the local wallet (private @@ -952,6 +960,7 @@ {"vout", UniValueType(UniValue::VNUM)}, {"scriptPubKey", UniValueType(UniValue::VSTR)}, {"redeemScript", UniValueType(UniValue::VSTR)}, + {"amount", UniValueType(UniValue::VSTR)}, }); UniValue v = find_value(prevOut, "redeemScript"); if (!v.isNull()) { diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -135,6 +135,7 @@ "8f50977c8493f3\"," "\"vout\":1,\"scriptPubKey\":" "\"a914b10c9df5f7edf436c697f02f1efdba4cf399615187\"," + "\"amount\":\"3.14159\"," "\"redeemScript\":" "\"512103debedc17b3df2badbcdd86d5feb4562b86fe182e5998" "abd8bcd4f122c6155b1b21027e940bb73ab8732bfdf7f9216ece" @@ -154,6 +155,45 @@ BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); } +BOOST_AUTO_TEST_CASE(rpc_rawsign_missing_amount) { + // Old format, missing amount parameter for prevout should generate + // an RPC error. This is because of new replay-protected tx's require + // nonzero amount present in signed tx. + // See: https://github.com/Bitcoin-ABC/bitcoin-abc/issues/63 + // (We will re-use the tx + keys from the above rpc_rawsign test for + // simplicity.) + UniValue r; + std::string prevout = "[{\"txid\":" + "\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b724" + "8f50977c8493f3\"," + "\"vout\":1,\"scriptPubKey\":" + "\"a914b10c9df5f7edf436c697f02f1efdba4cf399615187\"," + "\"redeemScript\":" + "\"512103debedc17b3df2badbcdd86d5feb4562b86fe182e5998" + "abd8bcd4f122c6155b1b21027e940bb73ab8732bfdf7f9216ece" + "fca5b94d6df834e77e108f68e66f126044c052ae\"}]"; + r = CallRPC(std::string("createrawtransaction ") + prevout + " " + + "{\"3HqAe9LtNBjnsfM4CyYaWTnvCaUYT7v4oZ\":11}"); + std::string notsigned = r.get_str(); + std::string privkey1 = + "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\""; + std::string privkey2 = + "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\""; + bool exceptionThrownDueToMissingAmount = false, + errorWasMissingAmount = false; + try { + r = CallRPC(std::string("signrawtransaction ") + notsigned + " " + + prevout + " " + "[" + privkey1 + "," + privkey2 + "]"); + } catch (const std::runtime_error &e) { + exceptionThrownDueToMissingAmount = true; + if (std::string(e.what()).find("amount") != std::string::npos) { + errorWasMissingAmount = true; + } + } + BOOST_CHECK(exceptionThrownDueToMissingAmount == true); + BOOST_CHECK(errorWasMissingAmount == true); +} + BOOST_AUTO_TEST_CASE(rpc_createraw_op_return) { BOOST_CHECK_NO_THROW( CallRPC("createrawtransaction "