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