Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 71 additions & 42 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,55 @@ void SendMoney(const CTxDestination& address, CAmount nValue, CWalletTx& wtxNew)
throw JSONRPCError(RPC_WALLET_ERROR, res.ToString());
}

static SaplingOperation CreateShieldedTransaction(const JSONRPCRequest& request);

/*
* redirect sendtoaddress/sendmany inputs to shieldedsendmany implementation (CreateShieldedTransaction)
*/
static UniValue ShieldedSendManyTo(const UniValue& sendTo,
const std::string& commentStr,
const std::string& toStr,
int nMinDepth,
bool fIncludeDelegated)
{
// convert params to 'shieldedsendmany' format
JSONRPCRequest req;
req.params = UniValue(UniValue::VARR);
if (!fIncludeDelegated) {
req.params.push_back(UniValue("from_transparent"));
} else {
req.params.push_back(UniValue("from_trans_cold"));
}
UniValue recipients(UniValue::VARR);
for (const std::string& key : sendTo.getKeys()) {
UniValue recipient(UniValue::VOBJ);
recipient.pushKV("address", key);
recipient.pushKV("amount", sendTo[key]);
recipients.push_back(recipient);
}
req.params.push_back(recipients);
req.params.push_back(nMinDepth);

// send
SaplingOperation operation = CreateShieldedTransaction(req);
std::string txid;
auto res = operation.send(txid);
if (!res)
throw JSONRPCError(RPC_WALLET_ERROR, res.getError());

// add comments
const uint256 txHash(txid);
assert(pwalletMain->mapWallet.count(txHash));
if (!commentStr.empty()) {
pwalletMain->mapWallet[txHash].mapValue["comment"] = commentStr;
}
if (!toStr.empty()) {
pwalletMain->mapWallet[txHash].mapValue["to"] = toStr;
}

return txid;
}

UniValue sendtoaddress(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
Expand All @@ -958,22 +1007,34 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
HelpExampleCli("sendtoaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\" 0.1 \"donation\" \"seans outpost\"") +
HelpExampleRpc("sendtoaddress", "\"DMJRSsuU9zfyrvxVaAEFQqK4MxZg6vgeS6\", 0.1, \"donation\", \"seans outpost\""));

LOCK2(cs_main, pwalletMain->cs_wallet);

bool isStaking = false;
CTxDestination address = DecodeDestination(request.params[0].get_str(), isStaking);
if (!IsValidDestination(address) || isStaking)
bool isStaking = false, isShielded = false;
const std::string addrStr = request.params[0].get_str();
const CWDestination& destination = Standard::DecodeDestination(addrStr, isStaking, isShielded);
if (!Standard::IsValidDestination(destination) || isStaking)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid PIVX address");
const std::string commentStr = (request.params.size() > 2 && !request.params[2].isNull()) ?
request.params[2].get_str() : "";
const std::string toStr = (request.params.size() > 3 && !request.params[3].isNull()) ?
request.params[3].get_str() : "";

if (isShielded) {
UniValue sendTo(UniValue::VOBJ);
sendTo.pushKV(addrStr, request.params[1]);
return ShieldedSendManyTo(sendTo, commentStr, toStr, 1, false);
}

const CTxDestination& address = *Standard::GetTransparentDestination(destination);
LOCK2(cs_main, pwalletMain->cs_wallet);

// Amount
CAmount nAmount = AmountFromValue(request.params[1]);

// Wallet comments
CWalletTx wtx;
if (request.params.size() > 2 && !request.params[2].isNull() && !request.params[2].get_str().empty())
wtx.mapValue["comment"] = request.params[2].get_str();
if (request.params.size() > 3 && !request.params[3].isNull() && !request.params[3].get_str().empty())
wtx.mapValue["to"] = request.params[3].get_str();
if (!commentStr.empty())
wtx.mapValue["comment"] = commentStr;
if (!toStr.empty())
wtx.mapValue["to"] = toStr;

EnsureWalletIsUnlocked();

Expand Down Expand Up @@ -2128,39 +2189,7 @@ UniValue sendmany(const JSONRPCRequest& request)
}

if (fShieldSend) {
// convert params to 'shieldedsendmany' format
JSONRPCRequest req;
req.params = UniValue(UniValue::VARR);
if (!fIncludeDelegated) {
req.params.push_back(UniValue("from_transparent"));
} else {
req.params.push_back(UniValue("from_trans_cold"));
}
UniValue recipients(UniValue::VARR);
for (const std::string& key : sendTo.getKeys()) {
UniValue recipient(UniValue::VOBJ);
recipient.pushKV("address", key);
recipient.pushKV("amount", sendTo[key]);
recipients.push_back(recipient);
}
req.params.push_back(recipients);
req.params.push_back(nMinDepth);

// send
SaplingOperation operation = CreateShieldedTransaction(req);
std::string txid;
auto res = operation.send(txid);
if (!res)
throw JSONRPCError(RPC_WALLET_ERROR, res.getError());

// add comment
if (!comment.empty()) {
const uint256 txHash(txid);
assert(pwalletMain->mapWallet.count(txHash));
pwalletMain->mapWallet[txHash].mapValue["comment"] = comment;
}

return txid;
return ShieldedSendManyTo(sendTo, comment, "", nMinDepth, fIncludeDelegated);
}

// All recipients are transparent: use Legacy sendmany t->t
Expand Down
24 changes: 24 additions & 0 deletions test/functional/sapling_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,31 @@ def run_test(self):
# Balance of node 0 is: prev_balance - 1 PIV (+fee) sent externally + 250 PIV matured coinbase
assert_equal(self.nodes[0].getbalance(), satoshi_round(prev_balance + Decimal('249') - Decimal(fee)))

# Now shield some funds using sendtoaddress
self.log.info("TX12: Shielding coins with sendtoaddress RPC...")
prev_balance = self.nodes[0].getbalance()
mytxid12 = self.nodes[0].sendtoaddress(saplingAddr0, Decimal('10'))
self.check_tx_priority([mytxid12])
self.log.info("Done. Checking details and balances...")

# Decrypted transaction details should be correct
pt = self.nodes[0].viewshieldedtransaction(mytxid12)
fee = pt["fee"]
assert_equal(pt['txid'], mytxid12)
assert_equal(len(pt['spends']), 0)
assert_equal(len(pt['outputs']), 1)
out = pt['outputs'][0]
assert_equal(out['address'], saplingAddr0)
assert_equal(out['outgoing'], False)
assert_equal(out['value'], Decimal('10'))

# Verify balance
self.nodes[2].generate(1)
self.sync_all()
assert_equal(self.nodes[0].getshieldedbalance(saplingAddr0), Decimal('29')) # 19 prev balance + 10 received

self.log.info("All good.")


if __name__ == '__main__':
WalletSaplingTest().main()