Skip to content

Commit

Permalink
Port rpc spend
Browse files Browse the repository at this point in the history
  • Loading branch information
timemarkovqtum committed Jul 2, 2024
1 parent e53ee0f commit 92092d3
Show file tree
Hide file tree
Showing 6 changed files with 744 additions and 55 deletions.
8 changes: 8 additions & 0 deletions src/rpc/rawtransaction_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
}
}

void CheckSenderSignatures(CMutableTransaction& mtx)
{
}

void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)
{
int nHashType = ParseSighashString(hashType);
Expand Down Expand Up @@ -334,3 +338,7 @@ void SignTransactionResultToJSON(CMutableTransaction& mtx, bool complete, const
result.pushKV("errors", vErrors);
}
}

void SignTransactionOutputResultToJSON(CMutableTransaction &mtx, bool complete, std::map<int, std::string> &output_errors, UniValue &result)
{
}
4 changes: 4 additions & 0 deletions src/rpc/rawtransaction_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,8 @@ void AddOutputs(CMutableTransaction& rawTx, const UniValue& outputs_in);
/** Create a transaction from univalue parameters */
CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniValue& outputs_in, const UniValue& locktime, std::optional<bool> rbf);

void SignTransactionOutputResultToJSON(CMutableTransaction& mtx, bool complete, std::map<int, std::string>& output_errors, UniValue& result);

void CheckSenderSignatures(CMutableTransaction& mtx);

#endif // BITCOIN_RPC_RAWTRANSACTION_UTIL_H
140 changes: 126 additions & 14 deletions src/wallet/rpc/addresses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,23 @@
#include <wallet/receive.h>
#include <wallet/rpc/util.h>
#include <wallet/wallet.h>
#include <wallet/rpc/addresses.h>

#include <univalue.h>

namespace wallet {
RPCHelpMan getnewaddress()
{
return RPCHelpMan{"getnewaddress",
"\nReturns a new Bitcoin address for receiving payments.\n"
"\nReturns a new Qtum address for receiving payments.\n"
"If 'label' is specified, it is added to the address book \n"
"so payments received with the address will be associated with 'label'.\n",
{
{"label", RPCArg::Type::STR, RPCArg::Default{""}, "The label name for the address to be linked to. It can also be set to the empty string \"\" to represent the default label. The label does not need to exist, it will be created if there is no label by the given name."},
{"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -addresstype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
},
RPCResult{
RPCResult::Type::STR, "address", "The new bitcoin address"
RPCResult::Type::STR, "address", "The new qtum address"
},
RPCExamples{
HelpExampleCli("getnewaddress", "")
Expand Down Expand Up @@ -75,7 +76,7 @@ RPCHelpMan getnewaddress()
RPCHelpMan getrawchangeaddress()
{
return RPCHelpMan{"getrawchangeaddress",
"\nReturns a new Bitcoin address, for receiving change.\n"
"\nReturns a new Qtum address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n",
{
{"address_type", RPCArg::Type::STR, RPCArg::DefaultHint{"set by -changetype"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", \"bech32\", and \"bech32m\"."},
Expand Down Expand Up @@ -124,7 +125,7 @@ RPCHelpMan setlabel()
return RPCHelpMan{"setlabel",
"\nSets the label associated with the given address.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to be associated with a label."},
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address to be associated with a label."},
{"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label to assign to the address."},
},
RPCResult{RPCResult::Type::NONE, "", ""},
Expand All @@ -141,7 +142,7 @@ RPCHelpMan setlabel()

CTxDestination dest = DecodeDestination(request.params[0].get_str());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Qtum address");
}

const std::string label{LabelFromValue(request.params[1])};
Expand Down Expand Up @@ -171,7 +172,7 @@ RPCHelpMan listaddressgroupings()
{
{RPCResult::Type::ARR_FIXED, "", "",
{
{RPCResult::Type::STR, "address", "The bitcoin address"},
{RPCResult::Type::STR, "address", "The qtum address"},
{RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
{RPCResult::Type::STR, "label", /*optional=*/true, "The label"},
}},
Expand Down Expand Up @@ -221,16 +222,16 @@ RPCHelpMan addmultisigaddress()
{
return RPCHelpMan{"addmultisigaddress",
"\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
"Each key is a Qtum address or hex-encoded public key.\n"
"This functionality is only intended for use with non-watchonly addresses.\n"
"See `importaddress` for watchonly p2sh address support.\n"
"If 'label' is specified, assign address to that label.\n"
"Note: This command is only compatible with legacy wallets.\n",
{
{"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys or addresses."},
{"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The bitcoin addresses or hex-encoded public keys",
{"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "The qtum addresses or hex-encoded public keys",
{
{"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "bitcoin address or hex-encoded public key"},
{"key", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "qtum address or hex-encoded public key"},
},
},
{"label", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "A label to assign the addresses to."},
Expand Down Expand Up @@ -482,7 +483,7 @@ class DescribeWalletAddressVisitor
UniValue operator()(const WitnessUnknown& id) const { return UniValue(UniValue::VOBJ); }
};

static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestination& dest)
UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestination& dest)
{
UniValue ret(UniValue::VOBJ);
UniValue detail = DescribeAddress(dest);
Expand All @@ -497,15 +498,15 @@ static UniValue DescribeWalletAddress(const CWallet& wallet, const CTxDestinatio
RPCHelpMan getaddressinfo()
{
return RPCHelpMan{"getaddressinfo",
"\nReturn information about the given bitcoin address.\n"
"\nReturn information about the given qtum address.\n"
"Some of the information will only be present if the address is in the active wallet.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The qtum address for which to get information."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "address", "The bitcoin address validated."},
{RPCResult::Type::STR, "address", "The qtum address validated."},
{RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address."},
{RPCResult::Type::BOOL, "ismine", "If the address is yours."},
{RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
Expand Down Expand Up @@ -762,7 +763,7 @@ RPCHelpMan walletdisplayaddress()
"walletdisplayaddress",
"Display address on an external signer for verification.",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "bitcoin address to display"},
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "qtum address to display"},
},
RPCResult{
RPCResult::Type::OBJ,"","",
Expand Down Expand Up @@ -797,4 +798,115 @@ RPCHelpMan walletdisplayaddress()
};
}
#endif // ENABLE_EXTERNAL_SIGNER

///////////////////////////////////////////////////////////////////////
bool getAddressToPubKey(const CWallet& wallet, std::string addr, std::string& pubkey)
{
CTxDestination dest = DecodeDestination(addr);
// Make sure the destination is valid
if (!IsValidDestination(dest)) {
return false;
}
UniValue detail = DescribeWalletAddress(wallet, dest);
if(detail.exists("pubkey") && detail["pubkey"].isStr())
{
pubkey = detail["pubkey"].get_str();
return true;
}

return false;
}

RPCHelpMan createmultisig()
{
return RPCHelpMan{"createmultisig",
"\nCreates a multi-signature address with n signature of m keys required.\n"
"It returns a json object with the address and redeemScript.\n",
{
{"nrequired", RPCArg::Type::NUM, RPCArg::Optional::NO, "The number of required signatures out of the n keys."},
{"keys", RPCArg::Type::ARR, RPCArg::Optional::NO, "A json array of keys which are qtum addresses or hex-encoded public keys.",
{
{"key", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, "qtum address or hex-encoded public key"},
}},
{"address_type", RPCArg::Type::STR, RPCArg::Default{"legacy"}, "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
RPCResult::Type::OBJ, "", "",
{
{RPCResult::Type::STR, "address", "The value of the new multisig address."},
{RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script."},
{RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
{RPCResult::Type::ARR, "warnings", /* optional */ true, "Any warnings resulting from the creation of this multisig",
{
{RPCResult::Type::STR, "", ""},
}},
}
},
RPCExamples{
"\nCreate a multisig address from 2 public keys\n"
+ HelpExampleCli("createmultisig", "2 \"[\\\"QjWnDZxwLhrJDcp4Hisse8RfBo2jRDZY5Z\\\",\\\"Q6sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\"]\"") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("createmultisig", "2, [\"QjWnDZxwLhrJDcp4Hisse8RfBo2jRDZY5Z\",\"Q6sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\"]")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{

std::shared_ptr<CWallet> const pwallet = wallet::GetWalletForJSONRPCRequest(request);
if (!pwallet) return NullUniValue;

LOCK(pwallet->cs_wallet);
std::string pubkey;

int required = request.params[0].getInt<int>();

// Get the public keys
const UniValue& keys = request.params[1].get_array();
std::vector<CPubKey> pubkeys;
for (unsigned int i = 0; i < keys.size(); ++i) {
if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) {
pubkeys.push_back(HexToPubKey(keys[i].get_str()));
} else if (getAddressToPubKey(*pwallet, keys[i].get_str(), pubkey)){
pubkeys.push_back(HexToPubKey(pubkey));
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n.", keys[i].get_str()));
}
}

// Get the output type
OutputType output_type = OutputType::LEGACY;
if (!request.params[2].isNull()) {
std::optional<OutputType> parsed = ParseOutputType(request.params[2].get_str());
if (!parsed) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
} else if (parsed.value() == OutputType::BECH32M) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "createmultisig cannot create bech32m multisig addresses");
}
output_type = parsed.value();
}

// Construct using pay-to-script-hash:
FillableSigningProvider keystore;
CScript inner;
const CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, keystore, inner);

// Make the descriptor
std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), keystore);

UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest));
result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());

UniValue warnings(UniValue::VARR);
if (descriptor->GetOutputType() != output_type) {
// Only warns if the user has explicitly chosen an address type we cannot generate
warnings.push_back("Unable to make chosen address type, please ensure no uncompressed public keys are present.");
}
PushWarnings(warnings, result);

return result;
},
};
}

} // namespace wallet
Loading

0 comments on commit 92092d3

Please sign in to comment.