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 <interfaces/chain.h>
12 : #include <key_io.h>
13 : #include <policy/policy.h>
14 : #include <primitives/transaction.h>
15 : #include <rpc/request.h>
16 : #include <rpc/util.h>
17 : #include <script/signingprovider.h>
18 : #include <univalue.h>
19 : #include <util/strencodings.h>
20 : #include <validation.h>
21 : #include <txmempool.h>
22 : #include <util/translation.h>
23 :
24 8 : CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime)
25 : {
26 8 : if (outputs_in.isNull()) {
27 4 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, output argument must be non-null");
28 : }
29 :
30 7 : UniValue inputs;
31 7 : if (inputs_in.isNull()) {
32 0 : inputs = UniValue::VARR;
33 0 : } else {
34 7 : inputs = inputs_in.get_array();
35 : }
36 :
37 7 : const bool outputs_is_obj = outputs_in.isObject();
38 7 : UniValue outputs = outputs_is_obj ? outputs_in.get_obj() : outputs_in.get_array();
39 :
40 7 : CMutableTransaction rawTx;
41 7 : if (!locktime.isNull()) {
42 0 : int64_t nLockTime = locktime.getInt<int64_t>();
43 0 : if (nLockTime < 0 || nLockTime > LOCKTIME_MAX)
44 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
45 0 : rawTx.nLockTime = nLockTime;
46 0 : }
47 :
48 13 : for (unsigned int idx = 0; idx < inputs.size(); idx++) {
49 6 : const UniValue& input = inputs[idx];
50 6 : const UniValue& o = input.get_obj();
51 :
52 6 : uint256 txid = ParseHashO(o, "txid");
53 :
54 6 : const UniValue& vout_v = o.find_value("vout");
55 6 : if (!vout_v.isNum())
56 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
57 6 : int nOutput = vout_v.getInt<int>();
58 6 : if (nOutput < 0)
59 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative");
60 :
61 : uint32_t nSequence;
62 6 : if (rawTx.nLockTime) {
63 0 : nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; /* CTxIn::SEQUENCE_FINAL - 1 */
64 0 : } else {
65 6 : nSequence = CTxIn::SEQUENCE_FINAL;
66 : }
67 :
68 : // set the sequence number if passed in the parameters object
69 6 : const UniValue& sequenceObj = o.find_value("sequence");
70 6 : if (sequenceObj.isNum()) {
71 0 : int64_t seqNr64 = sequenceObj.getInt<int64_t>();
72 0 : if (seqNr64 < 0 || seqNr64 > CTxIn::SEQUENCE_FINAL)
73 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
74 : else
75 0 : nSequence = (uint32_t)seqNr64;
76 0 : }
77 :
78 6 : CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
79 :
80 6 : rawTx.vin.push_back(in);
81 6 : }
82 :
83 7 : if (!outputs_is_obj) {
84 : // Translate array of key-value pairs into dict
85 0 : UniValue outputs_dict = UniValue(UniValue::VOBJ);
86 0 : for (size_t i = 0; i < outputs.size(); ++i) {
87 0 : const UniValue& output = outputs[i];
88 0 : if (!output.isObject()) {
89 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
90 : }
91 0 : if (output.size() != 1) {
92 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
93 : }
94 0 : outputs_dict.pushKVs(output);
95 0 : }
96 0 : outputs = std::move(outputs_dict);
97 0 : }
98 :
99 : // Duplicate checking
100 7 : std::set<CTxDestination> destinations;
101 7 : bool has_data{false};
102 :
103 10 : for (const std::string& name_ : outputs.getKeys()) {
104 6 : if (name_ == "data") {
105 4 : if (has_data) {
106 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
107 : }
108 4 : has_data = true;
109 4 : std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
110 :
111 2 : CTxOut out(0, CScript() << OP_RETURN << data);
112 2 : rawTx.vout.push_back(out);
113 2 : } else {
114 2 : CTxDestination destination = DecodeDestination(name_);
115 2 : if (!IsValidDestination(destination)) {
116 1 : throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + name_);
117 : }
118 :
119 1 : if (!destinations.insert(destination).second) {
120 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_);
121 : }
122 :
123 1 : CScript scriptPubKey = GetScriptForDestination(destination);
124 1 : CAmount nAmount = AmountFromValue(outputs[name_]);
125 :
126 1 : CTxOut out(nAmount, scriptPubKey);
127 1 : rawTx.vout.push_back(out);
128 1 : }
129 : }
130 :
131 4 : return rawTx;
132 11 : }
133 :
134 : /** Pushes a JSON object for script verification or signing errors to vErrorsRet. */
135 1 : static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage)
136 : {
137 1 : UniValue entry(UniValue::VOBJ);
138 1 : entry.pushKV("txid", txin.prevout.hash.ToString());
139 1 : entry.pushKV("vout", (uint64_t)txin.prevout.n);
140 1 : entry.pushKV("scriptSig", HexStr(txin.scriptSig));
141 1 : entry.pushKV("sequence", (uint64_t)txin.nSequence);
142 1 : entry.pushKV("error", strMessage);
143 1 : vErrorsRet.push_back(entry);
144 1 : }
145 :
146 2 : void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keystore, std::map<COutPoint, Coin>& coins)
147 : {
148 2 : if (!prevTxsUnival.isNull()) {
149 2 : const UniValue& prevTxs = prevTxsUnival.get_array();
150 4 : for (unsigned int idx = 0; idx < prevTxs.size(); ++idx) {
151 2 : const UniValue& p = prevTxs[idx];
152 2 : if (!p.isObject()) {
153 0 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
154 : }
155 :
156 2 : const UniValue& prevOut = p.get_obj();
157 :
158 4 : RPCTypeCheckObj(prevOut,
159 8 : {
160 2 : {"txid", UniValueType(UniValue::VSTR)},
161 2 : {"vout", UniValueType(UniValue::VNUM)},
162 2 : {"scriptPubKey", UniValueType(UniValue::VSTR)},
163 : });
164 :
165 2 : uint256 txid = ParseHashO(prevOut, "txid");
166 :
167 2 : int nOut = prevOut.find_value("vout").getInt<int>();
168 2 : if (nOut < 0) {
169 0 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout cannot be negative");
170 : }
171 :
172 2 : COutPoint out(txid, nOut);
173 2 : std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
174 2 : CScript scriptPubKey(pkData.begin(), pkData.end());
175 :
176 : {
177 2 : auto coin = coins.find(out);
178 2 : if (coin != coins.end() && !coin->second.IsSpent() && coin->second.out.scriptPubKey != scriptPubKey) {
179 0 : std::string err("Previous output scriptPubKey mismatch:\n");
180 0 : err = err + ScriptToAsmStr(coin->second.out.scriptPubKey) + "\nvs:\n"+
181 0 : ScriptToAsmStr(scriptPubKey);
182 0 : throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
183 0 : }
184 2 : Coin newcoin;
185 2 : newcoin.out.scriptPubKey = scriptPubKey;
186 2 : newcoin.out.nValue = 0;
187 2 : if (prevOut.exists("amount")) {
188 0 : newcoin.out.nValue = AmountFromValue(prevOut.find_value("amount"));
189 0 : }
190 2 : newcoin.nHeight = 1;
191 2 : coins[out] = std::move(newcoin);
192 2 : }
193 :
194 : // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed
195 2 : const bool is_p2sh = scriptPubKey.IsPayToScriptHash();
196 2 : if (keystore && is_p2sh) {
197 4 : RPCTypeCheckObj(prevOut,
198 4 : {
199 2 : {"redeemScript", UniValueType(UniValue::VSTR)},
200 : });
201 2 : const UniValue& rs{prevOut.find_value("redeemScript")};
202 2 : if (rs.isNull()) {
203 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript");
204 : }
205 :
206 2 : std::vector<unsigned char> scriptData(ParseHexV(rs, "redeemScript"));
207 2 : CScript script(scriptData.begin(), scriptData.end());
208 2 : keystore->AddCScript(script);
209 :
210 2 : if (is_p2sh) {
211 2 : const CTxDestination p2sh{ScriptHash(script)};
212 2 : if (scriptPubKey == GetScriptForDestination(p2sh)) {
213 : // traditional p2sh; arguably an error if
214 : // we got here with rs.IsNull(), because
215 : // that means the p2sh script was specified
216 : // via witnessScript param, but for now
217 : // we'll just quietly accept it
218 2 : } else {
219 : // otherwise, can't generate scriptPubKey from
220 : // either script, so we got unusable parameters
221 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript does not match scriptPubKey");
222 : }
223 2 : }
224 2 : if (rs.isNull()) {
225 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript");
226 : }
227 2 : }
228 2 : }
229 2 : }
230 2 : }
231 :
232 2 : void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)
233 : {
234 2 : int nHashType = ParseSighashString(hashType);
235 :
236 : // Script verification errors
237 2 : std::map<int, bilingual_str> input_errors;
238 :
239 2 : bool complete = SignTransaction(mtx, keystore, coins, nHashType, input_errors);
240 2 : SignTransactionResultToJSON(mtx, complete, coins, input_errors, result);
241 2 : }
242 :
243 2 : void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const std::map<COutPoint, Coin>& coins, const std::map<int, bilingual_str>& input_errors, UniValue& result)
244 : {
245 : // Make errors UniValue
246 2 : UniValue vErrors(UniValue::VARR);
247 3 : for (const auto& err_pair : input_errors) {
248 1 : if (err_pair.second.original == "Missing amount") {
249 : // This particular error needs to be an exception for some reason
250 0 : throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing amount for %s", coins.at(mtx.vin.at(err_pair.first).prevout).out.ToString()));
251 : }
252 1 : TxInErrorToJSON(mtx.vin.at(err_pair.first), vErrors, err_pair.second.original);
253 : }
254 :
255 2 : result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
256 2 : result.pushKV("complete", complete);
257 2 : if (!vErrors.empty()) {
258 1 : if (result.exists("errors")) {
259 0 : vErrors.push_backV(result["errors"].getValues());
260 0 : }
261 1 : result.pushKV("errors", vErrors);
262 1 : }
263 2 : }
|