rawtransaction_util.cpp
1 // Copyright (c) 2010 Satoshi Nakamoto 2 // Copyright (c) 2009-2022 The Bitcoin Core developers 3 // Distributed under the MIT software license, see the accompanying 4 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 6 #include <rpc/rawtransaction_util.h> 7 8 #include <coins.h> 9 #include <consensus/amount.h> 10 #include <core_io.h> 11 #include <key_io.h> 12 #include <policy/policy.h> 13 #include <primitives/transaction.h> 14 #include <rpc/request.h> 15 #include <rpc/util.h> 16 #include <script/sign.h> 17 #include <script/signingprovider.h> 18 #include <tinyformat.h> 19 #include <univalue.h> 20 #include <util/rbf.h> 21 #include <util/strencodings.h> 22 #include <util/translation.h> 23 24 void AddInputs(CMutableTransaction& rawTx, const UniValue& inputs_in, std::optional<bool> rbf) 25 { 26 UniValue inputs; 27 if (inputs_in.isNull()) { 28 inputs = UniValue::VARR; 29 } else { 30 inputs = inputs_in.get_array(); 31 } 32 33 for (unsigned int idx = 0; idx < inputs.size(); idx++) { 34 const UniValue& input = inputs[idx]; 35 const UniValue& o = input.get_obj(); 36 37 Txid txid = Txid::FromUint256(ParseHashO(o, "txid")); 38 39 const UniValue& vout_v = o.find_value("vout"); 40 if (!vout_v.isNum()) 41 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); 42 int nOutput = vout_v.getInt<int>(); 43 if (nOutput < 0) 44 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative"); 45 46 uint32_t nSequence; 47 48 if (rbf.value_or(true)) { 49 nSequence = MAX_BIP125_RBF_SEQUENCE; /* CTxIn::SEQUENCE_FINAL - 2 */ 50 } else if (rawTx.nLockTime) { 51 nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; /* CTxIn::SEQUENCE_FINAL - 1 */ 52 } else { 53 nSequence = CTxIn::SEQUENCE_FINAL; 54 } 55 56 // set the sequence number if passed in the parameters object 57 const UniValue& sequenceObj = o.find_value("sequence"); 58 if (sequenceObj.isNum()) { 59 int64_t seqNr64 = sequenceObj.getInt<int64_t>(); 60 if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL) { 61 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); 62 } else { 63 nSequence = (uint32_t)seqNr64; 64 } 65 } 66 67 CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); 68 69 rawTx.vin.push_back(in); 70 } 71 } 72 73 UniValue NormalizeOutputs(const UniValue& outputs_in) 74 { 75 if (outputs_in.isNull()) { 76 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null"); 77 } 78 79 const bool outputs_is_obj = outputs_in.isObject(); 80 UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array(); 81 82 if (!outputs_is_obj) { 83 // Translate array of key-value pairs into dict 84 UniValue outputs_dict = UniValue(UniValue::VOBJ); 85 for (size_t i = 0; i < outputs.size(); ++i) { 86 const UniValue& output = outputs[i]; 87 if (!output.isObject()) { 88 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected"); 89 } 90 if (output.size() != 1) { 91 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key"); 92 } 93 outputs_dict.pushKVs(output); 94 } 95 outputs = std::move(outputs_dict); 96 } 97 return outputs; 98 } 99 100 std::vector<std::pair<CTxDestination, CAmount>> ParseOutputs(const UniValue& outputs) 101 { 102 // Duplicate checking 103 std::set<CTxDestination> destinations; 104 std::vector<std::pair<CTxDestination, CAmount>> parsed_outputs; 105 bool has_data{false}; 106 for (const std::string& name_ : outputs.getKeys()) { 107 if (name_ == "data") { 108 if (has_data) { 109 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data"); 110 } 111 has_data = true; 112 std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data"); 113 CTxDestination destination{CNoDestination{CScript() << OP_RETURN << data}}; 114 CAmount amount{0}; 115 parsed_outputs.emplace_back(destination, amount); 116 } else { 117 CTxDestination destination{DecodeDestination(name_)}; 118 CAmount amount{AmountFromValue(outputs[name_])}; 119 if (!IsValidDestination(destination)) { 120 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); 121 } 122 123 if (!destinations.insert(destination).second) { 124 throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); 125 } 126 parsed_outputs.emplace_back(destination, amount); 127 } 128 } 129 return parsed_outputs; 130 } 131 132 void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in) 133 { 134 UniValue outputs(UniValue::VOBJ); 135 outputs = NormalizeOutputs(outputs_in); 136 137 std::vector<std::pair<CTxDestination, CAmount>> parsed_outputs = ParseOutputs(outputs); 138 for (const auto& [destination, nAmount] : parsed_outputs) { 139 CScript scriptPubKey = GetScriptForDestination(destination); 140 141 CTxOut out(nAmount, scriptPubKey); 142 rawTx.vout.push_back(out); 143 } 144 } 145 146 CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf) 147 { 148 CMutableTransaction rawTx; 149 150 if (!locktime.isNull()) { 151 int64_t nLockTime = locktime.getInt<int64_t>(); 152 if (nLockTime < 0 || nLockTime > LOCKTIME_MAX) 153 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); 154 rawTx.nLockTime = nLockTime; 155 } 156 157 AddInputs(rawTx, inputs_in, rbf); 158 AddOutputs(rawTx, outputs_in); 159 160 if (rbf.has_value() && rbf.value() && rawTx.vin.size() > 0 && !SignalsOptInRBF(CTransaction(rawTx))) { 161 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); 162 } 163 164 return rawTx; 165 } 166 167 /** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ 168 static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) 169 { 170 UniValue entry(UniValue::VOBJ); 171 entry.pushKV("txid", txin.prevout.hash.ToString()); 172 entry.pushKV("vout", (uint64_t)txin.prevout.n); 173 UniValue witness(UniValue::VARR); 174 for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) { 175 witness.push_back(HexStr(txin.scriptWitness.stack[i])); 176 } 177 entry.pushKV("witness", witness); 178 entry.pushKV("scriptSig", HexStr(txin.scriptSig)); 179 entry.pushKV("sequence", (uint64_t)txin.nSequence); 180 entry.pushKV("error", strMessage); 181 vErrorsRet.push_back(entry); 182 } 183 184 void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins) 185 { 186 if (!prevTxsUnival.isNull()) { 187 const UniValue& prevTxs = prevTxsUnival.get_array(); 188 for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) { 189 const UniValue& p = prevTxs[idx]; 190 if (!p.isObject()) { 191 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); 192 } 193 194 const UniValue& prevOut = p.get_obj(); 195 196 RPCTypeCheckObj(prevOut, 197 { 198 {"txid", UniValueType(UniValue::VSTR)}, 199 {"vout", UniValueType(UniValue::VNUM)}, 200 {"scriptPubKey", UniValueType(UniValue::VSTR)}, 201 }); 202 203 Txid txid = Txid::FromUint256(ParseHashO(prevOut, "txid")); 204 205 int nOut = prevOut.find_value("vout").getInt<int>(); 206 if (nOut < 0) { 207 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout cannot be negative"); 208 } 209 210 COutPoint out(txid, nOut); 211 std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); 212 CScript scriptPubKey(pkData.begin(), pkData.end()); 213 214 { 215 auto coin = coins.find(out); 216 if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) { 217 std::string err("Previous output scriptPubKey mismatch:\n"); 218 err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+ 219 ScriptToAsmStr(scriptPubKey); 220 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); 221 } 222 Coin newcoin; 223 newcoin.out.scriptPubKey = scriptPubKey; 224 newcoin.out.nValue = MAX_MONEY; 225 if (prevOut.exists("amount")) { 226 newcoin.out.nValue = AmountFromValue(prevOut.find_value("amount")); 227 } 228 newcoin.nHeight = 1; 229 coins[out] = std::move(newcoin); 230 } 231 232 // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed 233 const bool is_p2sh = scriptPubKey.IsPayToScriptHash(); 234 const bool is_p2wsh = scriptPubKey.IsPayToWitnessScriptHash(); 235 if (keystore && (is_p2sh || is_p2wsh)) { 236 RPCTypeCheckObj(prevOut, 237 { 238 {"redeemScript", UniValueType(UniValue::VSTR)}, 239 {"witnessScript", UniValueType(UniValue::VSTR)}, 240 }, true); 241 const UniValue& rs{prevOut.find_value("redeemScript")}; 242 const UniValue& ws{prevOut.find_value("witnessScript")}; 243 if (rs.isNull() && ws.isNull()) { 244 throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript/witnessScript"); 245 } 246 247 // work from witnessScript when possible 248 std::vector<unsigned char> scriptData(!ws.isNull() ? ParseHexV(ws, "witnessScript") : ParseHexV(rs, "redeemScript")); 249 CScript script(scriptData.begin(), scriptData.end()); 250 keystore->AddCScript(script); 251 // Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH). 252 // This is done for redeemScript only for compatibility, it is encouraged to use the explicit witnessScript field instead. 253 CScript witness_output_script{GetScriptForDestination(WitnessV0ScriptHash(script))}; 254 keystore->AddCScript(witness_output_script); 255 256 if (!ws.isNull() && !rs.isNull()) { 257 // if both witnessScript and redeemScript are provided, 258 // they should either be the same (for backwards compat), 259 // or the redeemScript should be the encoded form of 260 // the witnessScript (ie, for p2sh-p2wsh) 261 if (ws.get_str() != rs.get_str()) { 262 std::vector<unsigned char> redeemScriptData(ParseHexV(rs, "redeemScript")); 263 CScript redeemScript(redeemScriptData.begin(), redeemScriptData.end()); 264 if (redeemScript != witness_output_script) { 265 throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript does not correspond to witnessScript"); 266 } 267 } 268 } 269 270 if (is_p2sh) { 271 const CTxDestination p2sh{ScriptHash(script)}; 272 const CTxDestination p2sh_p2wsh{ScriptHash(witness_output_script)}; 273 if (scriptPubKey == GetScriptForDestination(p2sh)) { 274 // traditional p2sh; arguably an error if 275 // we got here with rs.IsNull(), because 276 // that means the p2sh script was specified 277 // via witnessScript param, but for now 278 // we'll just quietly accept it 279 } else if (scriptPubKey == GetScriptForDestination(p2sh_p2wsh)) { 280 // p2wsh encoded as p2sh; ideally the witness 281 // script was specified in the witnessScript 282 // param, but also support specifying it via 283 // redeemScript param for backwards compat 284 // (in which case ws.IsNull() == true) 285 } else { 286 // otherwise, can't generate scriptPubKey from 287 // either script, so we got unusable parameters 288 throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey"); 289 } 290 } else if (is_p2wsh) { 291 // plain p2wsh; could throw an error if script 292 // was specified by redeemScript rather than 293 // witnessScript (ie, ws.IsNull() == true), but 294 // accept it for backwards compat 295 const CTxDestination p2wsh{WitnessV0ScriptHash(script)}; 296 if (scriptPubKey != GetScriptForDestination(p2wsh)) { 297 throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript/witnessScript does not match scriptPubKey"); 298 } 299 } 300 } 301 } 302 } 303 } 304 305 void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result) 306 { 307 int nHashType = ParseSighashString(hashType); 308 309 // Script verification errors 310 std::map<int, bilingual_str> input_errors; 311 312 bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors); 313 SignTransactionResultToJSON(mtx, complete, coins, input_errors, result); 314 } 315 316 void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, bilingual_str>& input_errors, UniValue& result) 317 { 318 // Make errors UniValue 319 UniValue vErrors(UniValue::VARR); 320 for (const auto& err_pair : input_errors) { 321 if (err_pair.second.original == "Missing amount") { 322 // This particular error needs to be an exception for some reason 323 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coins.at(mtx.vin.at(err_pair.first).prevout).out.ToString())); 324 } 325 TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second.original); 326 } 327 328 result.pushKV("hex", EncodeHexTx(CTransaction(mtx))); 329 result.pushKV("complete", complete); 330 if (!vErrors.empty()) { 331 if (result.exists("errors")) { 332 vErrors.push_backV(result["errors"].getValues()); 333 } 334 result.pushKV("errors", vErrors); 335 } 336 }