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