diff --git a/qa/rpc-tests/wallet_sapling.py b/qa/rpc-tests/wallet_sapling.py index 517869820..0ce1c734f 100755 --- a/qa/rpc-tests/wallet_sapling.py +++ b/qa/rpc-tests/wallet_sapling.py @@ -89,7 +89,7 @@ class WalletSaplingTest(BitcoinTestFramework): recipients.append({"address": saplingAddr0, "amount": Decimal('5')}) recipients.append({"address": taddr1, "amount": Decimal('5')}) myopid = self.nodes[1].z_sendmany(saplingAddr1, recipients, 1, 0) - wait_and_assert_operationid_status(self.nodes[1], myopid) + mytxid = wait_and_assert_operationid_status(self.nodes[1], myopid) self.sync_all() self.nodes[2].generate(1) @@ -100,5 +100,26 @@ class WalletSaplingTest(BitcoinTestFramework): assert_equal(self.nodes[1].z_getbalance(saplingAddr1), Decimal('5')) assert_equal(self.nodes[1].z_getbalance(taddr1), Decimal('5')) + # Verify existence of Sapling related JSON fields + resp = self.nodes[0].getrawtransaction(mytxid, 1) + assert_equal(resp['valueBalance'], Decimal('5')) + assert(len(resp['vShieldedSpend']) == 1) + assert(len(resp['vShieldedOutput']) == 2) + assert('bindingSig' in resp) + shieldedSpend = resp['vShieldedSpend'][0] + assert('cv' in shieldedSpend) + assert('anchor' in shieldedSpend) + assert('nullifier' in shieldedSpend) + assert('rk' in shieldedSpend) + assert('proof' in shieldedSpend) + assert('spendAuthSig' in shieldedSpend) + shieldedOutput = resp['vShieldedOutput'][0] + assert('cv' in shieldedOutput) + assert('cmu' in shieldedOutput) + assert('ephemeralKey' in shieldedOutput) + assert('encCiphertext' in shieldedOutput) + assert('outCiphertext' in shieldedOutput) + assert('proof' in shieldedOutput) + if __name__ == '__main__': WalletSaplingTest().main() diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 912cbe3ba..2aed45097 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -114,6 +114,36 @@ UniValue TxJoinSplitToJSON(const CTransaction& tx) { return vjoinsplit; } +UniValue TxShieldedSpendsToJSON(const CTransaction& tx) { + UniValue vdesc(UniValue::VARR); + for (const SpendDescription& spendDesc : tx.vShieldedSpend) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("cv", spendDesc.cv.GetHex())); + obj.push_back(Pair("anchor", spendDesc.anchor.GetHex())); + obj.push_back(Pair("nullifier", spendDesc.nullifier.GetHex())); + obj.push_back(Pair("rk", spendDesc.rk.GetHex())); + obj.push_back(Pair("proof", HexStr(spendDesc.zkproof.begin(), spendDesc.zkproof.end()))); + obj.push_back(Pair("spendAuthSig", HexStr(spendDesc.spendAuthSig.begin(), spendDesc.spendAuthSig.end()))); + vdesc.push_back(obj); + } + return vdesc; +} + +UniValue TxShieldedOutputsToJSON(const CTransaction& tx) { + UniValue vdesc(UniValue::VARR); + for (const OutputDescription& outputDesc : tx.vShieldedOutput) { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("cv", outputDesc.cv.GetHex())); + obj.push_back(Pair("cmu", outputDesc.cm.GetHex())); + obj.push_back(Pair("ephemeralKey", outputDesc.ephemeralKey.GetHex())); + obj.push_back(Pair("encCiphertext", HexStr(outputDesc.encCiphertext.begin(), outputDesc.encCiphertext.end()))); + obj.push_back(Pair("outCiphertext", HexStr(outputDesc.outCiphertext.begin(), outputDesc.outCiphertext.end()))); + obj.push_back(Pair("proof", HexStr(outputDesc.zkproof.begin(), outputDesc.zkproof.end()))); + vdesc.push_back(obj); + } + return vdesc; +} + void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) { entry.push_back(Pair("txid", tx.GetHash().GetHex())); @@ -160,6 +190,17 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) UniValue vjoinsplit = TxJoinSplitToJSON(tx); entry.push_back(Pair("vjoinsplit", vjoinsplit)); + if (tx.fOverwintered && tx.nVersion >= SAPLING_TX_VERSION) { + entry.push_back(Pair("valueBalance", ValueFromAmount(tx.valueBalance))); + UniValue vspenddesc = TxShieldedSpendsToJSON(tx); + entry.push_back(Pair("vShieldedSpend", vspenddesc)); + UniValue voutputdesc = TxShieldedOutputsToJSON(tx); + entry.push_back(Pair("vShieldedOutput", voutputdesc)); + if (!(vspenddesc.empty() && voutputdesc.empty())) { + entry.push_back(Pair("bindingSig", HexStr(tx.bindingSig.begin(), tx.bindingSig.end()))); + } + } + if (!hashBlock.IsNull()) { entry.push_back(Pair("blockhash", hashBlock.GetHex())); BlockMap::iterator mi = mapBlockIndex.find(hashBlock);