void RPCTypeCheckObj(const UniValue& o, const std::map<std::string, UniValueType>& typesExpected, bool fAllowNull, bool fStrict) { for (const auto& t : typesExpected) { const UniValue& v = find_value(o, t.first); if (!fAllowNull && v.isNull()) throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) { std::string err = strprintf("Expected type %s for %s, got %s", uvTypeName(t.second.type), t.first, uvTypeName(v.type())); throw JSONRPCError(RPC_TYPE_ERROR, err); } } if (fStrict) { for (const std::string& k : o.getKeys()) { if (typesExpected.count(k) == 0) { std::string err = strprintf("Unexpected key %s", k); throw JSONRPCError(RPC_TYPE_ERROR, err); } } } }
UniValue createrawtransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 3 || params.size() > 4) throw runtime_error( "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime )\n" "\nCreate a transaction spending the given inputs and creating new outputs.\n" "Outputs can be addresses or data.\n" "Returns hex-encoded raw transaction.\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network.\n" "\nArguments:\n" "1. \"transactions\" (string, required) A json array of json objects\n" " [\n" " {\n" " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n (numeric, required) The output number\n" " \"sequence\":n (numeric, optional) The sequence number\n" " }\n" " ,...\n" " ]\n" "2. \"outputs\" (string, required) a json object with outputs\n" " {\n" " \"address\": x.xxx (numeric or string, required) The key is the florincoin address, the numeric value (can be string) is the " + CURRENCY_UNIT + " amount\n" " \"data\": \"hex\", (string, required) The key is \"data\", the value is hex encoded data\n" " ...\n" " }\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" "4. tx-comment (string, optional) Transaction comment\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" "\nExamples\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") ); RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)(UniValue::VNUM), true); if (params[0].isNull() || params[1].isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); UniValue inputs = params[0].get_array(); UniValue sendTo = params[1].get_obj(); CMutableTransaction rawTx; // Florincoin: include tx-comment in rawTx if (params.size() == 4) { std::string txcomment = params[3].get_str(); if (txcomment.length() > CTransaction::MAX_TX_COMMENT_LEN_V2) { txcomment.resize(CTransaction::MAX_TX_COMMENT_LEN_V2); } rawTx.strTxComment = txcomment; } if (params.size() > 2 && !params[2].isNull()) { int64_t nLockTime = params[2].get_int64(); if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); rawTx.nLockTime = nLockTime; } for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; const UniValue& o = input.get_obj(); uint256 txid = ParseHashO(o, "txid"); const UniValue& vout_v = find_value(o, "vout"); if (!vout_v.isNum()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); int nOutput = vout_v.get_int(); if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits<uint32_t>::max() - 1 : std::numeric_limits<uint32_t>::max()); // set the sequence number if passed in the parameters object const UniValue& sequenceObj = find_value(o, "sequence"); if (sequenceObj.isNum()) { int64_t seqNr64 = sequenceObj.get_int64(); if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); else nSequence = (uint32_t)seqNr64; } CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); rawTx.vin.push_back(in); } set<CBitcoinAddress> setAddress; vector<string> addrList = sendTo.getKeys(); BOOST_FOREACH(const string& name_, addrList) { if (name_ == "data") { std::vector<unsigned char> data = ParseHexV(sendTo[name_].getValStr(),"Data"); CTxOut out(0, CScript() << OP_RETURN << data); rawTx.vout.push_back(out); } else { CBitcoinAddress address(name_); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Florincoin address: ")+name_); if (setAddress.count(address)) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); setAddress.insert(address); CScript scriptPubKey = GetScriptForDestination(address.Get()); CAmount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); } } return EncodeHexTx(rawTx); }
UniValue createrawtransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 2) throw runtime_error( "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...}\n" "\nCreate a transaction spending the given inputs and sending to the given addresses.\n" "Returns hex-encoded raw transaction.\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network.\n" "\nArguments:\n" "1. \"transactions\" (string, required) A json array of json objects\n" " [\n" " {\n" " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n (numeric, required) The output number\n" " }\n" " ,...\n" " ]\n" "2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n" " {\n" " \"address\": x.xxx (numeric, required) The key is the bitcoin address, the value is the btc amount\n" " ,...\n" " }\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" "\nExamples\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") ); LOCK(cs_main); RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)); UniValue inputs = params[0].get_array(); UniValue sendTo = params[1].get_obj(); CMutableTransaction rawTx; for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; const UniValue& o = input.get_obj(); uint256 txid = ParseHashO(o, "txid"); const UniValue& vout_v = find_value(o, "vout"); if (!vout_v.isNum()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); int nOutput = vout_v.get_int(); if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); CTxIn in(COutPoint(txid, nOutput)); rawTx.vin.push_back(in); } set<CBitcoinAddress> setAddress; vector<string> addrList = sendTo.getKeys(); BOOST_FOREACH(const string& name_, addrList) { CBitcoinAddress address(name_); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid Bitcoin address: ")+name_); if (setAddress.count(address)) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); setAddress.insert(address); CScript scriptPubKey = GetScriptForDestination(address.Get()); CAmount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); }
UniValue createrawtransaction(const UniValue& params, bool fHelp) { if (fHelp || (params.size() < 2 || params.size() > 3)) throw runtime_error( "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...} [tx-comment]\n" "Create a transaction spending given inputs\n" "(array of objects containing transaction id and output number),\n" "sending to given address(es).\n" "Each <amount> is either an amount to send or {\"count\":c,\"amount\":a,\"locktime\":t}\n" "which adds <c> outputs each sending <a> CON with a locktime of <t>.\n" "The count <c> and locktime <t> are both optional.\n" "Returns hex-encoded raw transaction.\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network."); RPCTypeCheck(params, list_of(UniValue::VARR)(UniValue::VOBJ)); UniValue inputs = params[0].get_array(); UniValue sendTo = params[1].get_obj(); CTransaction rawTx; // set conSpeech when creating a raw transaction if (params.size() == 3) rawTx.strCONSpeech = params[2].get_str(); else if (mapArgs["-conspeech"] != "off") rawTx.strCONSpeech = GetDefaultConSpeech(); if (rawTx.strCONSpeech.length() > MAX_TX_COMMENT_LEN) rawTx.strCONSpeech.resize(MAX_TX_COMMENT_LEN); for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; const UniValue& o = input.get_obj(); const UniValue& txid_v = find_value(o, "txid"); if (txid_v.isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key"); string txid = txid_v.get_str(); if (!IsHex(txid)) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); const UniValue& vout_v = find_value(o, "vout"); if (!vout_v.isNum()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); int nOutput = vout_v.get_int(); if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); CTxIn in(COutPoint(uint256(txid), nOutput)); rawTx.vin.push_back(in); } // set<CBitcoinAddress> setAddress; vector<string> addrList = sendTo.getKeys(); vector<UniValue> valueList= sendTo.getValues(); vector<UniValue>::iterator valueList_iterator = valueList.begin(); BOOST_FOREACH(const string& name_, addrList) { const UniValue& value_ = *valueList_iterator++; CBitcoinAddress address(name_); if (!address.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid PayCon address: ")+name_); // if (setAddress.count(address)) // throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); // setAddress.insert(address); CScript scriptPubKey; scriptPubKey.SetDestination(address.Get()); // we usually want the 2nd parameter to be {"addr1":ammount1,"addr2":ammount2,...} // in addition to numerical amounts we now also accept values like: // '{"count":2,"amount":1.3,"locktime":333333}' meaning 'make 2 outputs worth 1.3 CON each, locked until block 333333' // "count" is a non-negative integer; 0 works and simply makes no new outputs // "locktime" is a non-negative integer; values less than LOCKTIME_THRESHOLD (500,000,000) are block heights; others are epoch timestamps if (value_.isObject()) { const UniValue& o = value_.get_obj(); const UniValue& count_v = find_value(o, "count"); const UniValue& locktime_v = find_value(o, "locktime"); int64_t count, locktime; if (count_v.isNull()) count = 1; else if (count_v.isNum()) count = count_v.get_int64(); else throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, count must be integer"); if (count < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, count must not be negative"); if (!locktime_v.isNull()) { if (locktime_v.isNum()) locktime = locktime_v.get_int64(); else throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime must be integer"); if (locktime < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime must not be negative"); scriptPubKey.SetLockTime(locktime); } CTxOut out(AmountFromValue(find_value(o, "amount")), scriptPubKey); while (count--) rawTx.vout.push_back(out); } else { CTxOut out(AmountFromValue(value_), scriptPubKey); rawTx.vout.push_back(out); } } CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << rawTx; return HexStr(ss.begin(), ss.end()); }
UniValue createrawtransaction(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) throw std::runtime_error( "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime ) ( replaceable )\n" "\nCreate a transaction spending the given inputs and creating new outputs.\n" "Outputs can be addresses or data.\n" "Returns hex-encoded raw transaction.\n" "Note that the transaction's inputs are not signed, and\n" "it is not stored in the wallet or transmitted to the network.\n" "\nArguments:\n" "1. \"inputs\" (array, required) A json array of json objects\n" " [\n" " {\n" " \"txid\":\"id\", (string, required) The transaction id\n" " \"vout\":n, (numeric, required) The output number\n" " \"sequence\":n (numeric, optional) The sequence number\n" " } \n" " ,...\n" " ]\n" "2. \"outputs\" (object, required) a json object with outputs\n" " {\n" " \"address\": x.xxx, (numeric or string, required) The key is the bitcoin address, the numeric value (can be string) is the " + CURRENCY_UNIT + " amount\n" " \"data\": \"hex\" (string, required) The key is \"data\", the value is hex encoded data\n" " ,...\n" " }\n" "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n" "4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n" " Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n" "\nResult:\n" "\"transaction\" (string) hex string of the transaction\n" "\nExamples:\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"") ); RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ, UniValue::VNUM}, true); if (request.params[0].isNull() || request.params[1].isNull()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null"); UniValue inputs = request.params[0].get_array(); UniValue sendTo = request.params[1].get_obj(); CMutableTransaction rawTx; if (!request.params[2].isNull()) { int64_t nLockTime = request.params[2].get_int64(); if (nLockTime < 0 || nLockTime > std::numeric_limits<uint32_t>::max()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range"); rawTx.nLockTime = nLockTime; } bool rbfOptIn = request.params[3].isTrue(); for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; const UniValue& o = input.get_obj(); uint256 txid = ParseHashO(o, "txid"); const UniValue& vout_v = find_value(o, "vout"); if (!vout_v.isNum()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); int nOutput = vout_v.get_int(); if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); uint32_t nSequence; if (rbfOptIn) { nSequence = MAX_BIP125_RBF_SEQUENCE; } else if (rawTx.nLockTime) { nSequence = std::numeric_limits<uint32_t>::max() - 1; } else { nSequence = std::numeric_limits<uint32_t>::max(); } // set the sequence number if passed in the parameters object const UniValue& sequenceObj = find_value(o, "sequence"); if (sequenceObj.isNum()) { int64_t seqNr64 = sequenceObj.get_int64(); if (seqNr64 < 0 || seqNr64 > std::numeric_limits<uint32_t>::max()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range"); } else { nSequence = (uint32_t)seqNr64; } } CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence); rawTx.vin.push_back(in); } std::set<CTxDestination> destinations; std::vector<std::string> addrList = sendTo.getKeys(); for (const std::string& name_ : addrList) { if (name_ == "data") { std::vector<unsigned char> data = ParseHexV(sendTo[name_].getValStr(),"Data"); CTxOut out(0, CScript() << OP_RETURN << data); rawTx.vout.push_back(out); } else { CTxDestination destination = DecodeDestination(name_); if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); } if (!destinations.insert(destination).second) { throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); } CScript scriptPubKey = GetScriptForDestination(destination); CAmount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); } } if (!request.params[3].isNull() && rbfOptIn != SignalsOptInRBF(rawTx)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter combination: Sequence number(s) contradict replaceable option"); } return EncodeHexTx(rawTx); }