Auto merge of #3516 - str4d:3506-sendmany-sapling-t-ovk, r=ebfull
Generate an ovk to encrypt outCiphertext for t-addr senders Closes #3506.
This commit is contained in:
@@ -38,7 +38,7 @@ TEST(TransactionBuilder, Invoke)
|
|||||||
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
|
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
|
||||||
auto builder1 = TransactionBuilder(consensusParams, 1, &keystore);
|
auto builder1 = TransactionBuilder(consensusParams, 1, &keystore);
|
||||||
builder1.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
|
builder1.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
|
||||||
builder1.AddSaplingOutput(fvk_from, pk, 40000, {});
|
builder1.AddSaplingOutput(fvk_from.ovk, pk, 40000, {});
|
||||||
auto maybe_tx1 = builder1.Build();
|
auto maybe_tx1 = builder1.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx1), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx1), true);
|
||||||
auto tx1 = maybe_tx1.get();
|
auto tx1 = maybe_tx1.get();
|
||||||
@@ -73,7 +73,7 @@ TEST(TransactionBuilder, Invoke)
|
|||||||
// Check that trying to add a different anchor fails
|
// Check that trying to add a different anchor fails
|
||||||
ASSERT_FALSE(builder2.AddSaplingSpend(expsk, note, uint256(), witness));
|
ASSERT_FALSE(builder2.AddSaplingSpend(expsk, note, uint256(), witness));
|
||||||
|
|
||||||
builder2.AddSaplingOutput(fvk, pk, 25000, {});
|
builder2.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||||
auto maybe_tx2 = builder2.Build();
|
auto maybe_tx2 = builder2.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
||||||
auto tx2 = maybe_tx2.get();
|
auto tx2 = maybe_tx2.get();
|
||||||
@@ -153,7 +153,7 @@ TEST(TransactionBuilder, FailsWithNegativeChange)
|
|||||||
// Fail if there is only a Sapling output
|
// Fail if there is only a Sapling output
|
||||||
// 0.0005 z-ZEC out, 0.0001 t-ZEC fee
|
// 0.0005 z-ZEC out, 0.0001 t-ZEC fee
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
builder.AddSaplingOutput(fvk, pk, 50000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 50000, {});
|
||||||
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
EXPECT_FALSE(static_cast<bool>(builder.Build()));
|
||||||
|
|
||||||
// Fail if there is only a transparent output
|
// Fail if there is only a transparent output
|
||||||
@@ -237,7 +237,7 @@ TEST(TransactionBuilder, ChangeOutput)
|
|||||||
{
|
{
|
||||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
|
||||||
builder.SendChangeTo(zChangeAddr, fvkOut);
|
builder.SendChangeTo(zChangeAddr, fvkOut.ovk);
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
auto tx = maybe_tx.get();
|
auto tx = maybe_tx.get();
|
||||||
@@ -298,7 +298,7 @@ TEST(TransactionBuilder, SetFee)
|
|||||||
{
|
{
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder.AddSaplingOutput(fvk, pk, 25000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
auto tx = maybe_tx.get();
|
auto tx = maybe_tx.get();
|
||||||
@@ -315,7 +315,7 @@ TEST(TransactionBuilder, SetFee)
|
|||||||
{
|
{
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder.AddSaplingOutput(fvk, pk, 25000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||||
builder.SetFee(20000);
|
builder.SetFee(20000);
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
|
|||||||
@@ -1254,6 +1254,108 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling)
|
||||||
|
{
|
||||||
|
SelectParams(CBaseChainParams::REGTEST);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
||||||
|
|
||||||
|
LOCK(pwalletMain->cs_wallet);
|
||||||
|
|
||||||
|
if (!pwalletMain->HaveHDSeed()) {
|
||||||
|
pwalletMain->GenerateNewSeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
UniValue retValue;
|
||||||
|
|
||||||
|
// add keys manually
|
||||||
|
auto taddr = pwalletMain->GenerateNewKey().GetID();
|
||||||
|
std::string taddr1 = EncodeDestination(taddr);
|
||||||
|
auto pa = pwalletMain->GenerateNewSaplingZKey();
|
||||||
|
std::string zaddr1 = EncodePaymentAddress(pa);
|
||||||
|
|
||||||
|
auto consensusParams = Params().GetConsensus();
|
||||||
|
retValue = CallRPC("getblockcount");
|
||||||
|
int nextBlockHeight = retValue.get_int() + 1;
|
||||||
|
|
||||||
|
// Add a fake transaction to the wallet
|
||||||
|
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight);
|
||||||
|
CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(taddr) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||||
|
mtx.vout.push_back(CTxOut(5 * COIN, scriptPubKey));
|
||||||
|
CWalletTx wtx(pwalletMain, mtx);
|
||||||
|
pwalletMain->AddToWallet(wtx, true, NULL);
|
||||||
|
|
||||||
|
// Fake-mine the transaction
|
||||||
|
BOOST_CHECK_EQUAL(0, chainActive.Height());
|
||||||
|
CBlock block;
|
||||||
|
block.hashPrevBlock = chainActive.Tip()->GetBlockHash();
|
||||||
|
block.vtx.push_back(wtx);
|
||||||
|
block.hashMerkleRoot = block.BuildMerkleTree();
|
||||||
|
auto blockHash = block.GetHash();
|
||||||
|
CBlockIndex fakeIndex {block};
|
||||||
|
fakeIndex.nHeight = 1;
|
||||||
|
mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex));
|
||||||
|
chainActive.SetTip(&fakeIndex);
|
||||||
|
BOOST_CHECK(chainActive.Contains(&fakeIndex));
|
||||||
|
BOOST_CHECK_EQUAL(1, chainActive.Height());
|
||||||
|
wtx.SetMerkleBranch(block);
|
||||||
|
pwalletMain->AddToWallet(wtx, true, NULL);
|
||||||
|
|
||||||
|
// Context that z_sendmany requires
|
||||||
|
auto builder = TransactionBuilder(consensusParams, nextBlockHeight, pwalletMain);
|
||||||
|
mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight);
|
||||||
|
|
||||||
|
std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 1 * COIN, "ABCD") };
|
||||||
|
std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(builder, mtx, taddr1, {}, recipients, 0) );
|
||||||
|
std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation);
|
||||||
|
|
||||||
|
// Enable test mode so tx is not sent
|
||||||
|
static_cast<AsyncRPCOperation_sendmany *>(operation.get())->testmode = true;
|
||||||
|
|
||||||
|
// Generate the Sapling shielding transaction
|
||||||
|
operation->main();
|
||||||
|
BOOST_CHECK(operation->isSuccess());
|
||||||
|
|
||||||
|
// Get the transaction
|
||||||
|
auto result = operation->getResult();
|
||||||
|
BOOST_ASSERT(result.isObject());
|
||||||
|
auto hexTx = result["hex"].getValStr();
|
||||||
|
CDataStream ss(ParseHex(hexTx), SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
CTransaction tx;
|
||||||
|
ss >> tx;
|
||||||
|
BOOST_ASSERT(!tx.vShieldedOutput.empty());
|
||||||
|
|
||||||
|
// We shouldn't be able to decrypt with the empty ovk
|
||||||
|
BOOST_CHECK(!AttemptSaplingOutDecryption(
|
||||||
|
tx.vShieldedOutput[0].outCiphertext,
|
||||||
|
uint256(),
|
||||||
|
tx.vShieldedOutput[0].cv,
|
||||||
|
tx.vShieldedOutput[0].cm,
|
||||||
|
tx.vShieldedOutput[0].ephemeralKey));
|
||||||
|
|
||||||
|
// We should be able to decrypt the outCiphertext with the ovk
|
||||||
|
// generated for transparent addresses
|
||||||
|
HDSeed seed;
|
||||||
|
BOOST_ASSERT(pwalletMain->GetHDSeed(seed));
|
||||||
|
BOOST_CHECK(AttemptSaplingOutDecryption(
|
||||||
|
tx.vShieldedOutput[0].outCiphertext,
|
||||||
|
ovkForShieldingFromTaddr(seed),
|
||||||
|
tx.vShieldedOutput[0].cv,
|
||||||
|
tx.vShieldedOutput[0].cm,
|
||||||
|
tx.vShieldedOutput[0].ephemeralKey));
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
chainActive.SetTip(NULL);
|
||||||
|
mapBlockIndex.erase(blockHash);
|
||||||
|
mapArgs.erase("-developersapling");
|
||||||
|
mapArgs.erase("-experimentalfeatures");
|
||||||
|
|
||||||
|
// Revert to default
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||||
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This test covers storing encrypted zkeys in the wallet.
|
* This test covers storing encrypted zkeys in the wallet.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ bool TransactionBuilder::AddSaplingSpend(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TransactionBuilder::AddSaplingOutput(
|
void TransactionBuilder::AddSaplingOutput(
|
||||||
libzcash::SaplingFullViewingKey from,
|
uint256 ovk,
|
||||||
libzcash::SaplingPaymentAddress to,
|
libzcash::SaplingPaymentAddress to,
|
||||||
CAmount value,
|
CAmount value,
|
||||||
std::array<unsigned char, ZC_MEMO_SIZE> memo)
|
std::array<unsigned char, ZC_MEMO_SIZE> memo)
|
||||||
{
|
{
|
||||||
auto note = libzcash::SaplingNote(to, value);
|
auto note = libzcash::SaplingNote(to, value);
|
||||||
outputs.emplace_back(from.ovk, note, memo);
|
outputs.emplace_back(ovk, note, memo);
|
||||||
mtx.valueBalance -= value;
|
mtx.valueBalance -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,9 +84,9 @@ void TransactionBuilder::SetFee(CAmount fee)
|
|||||||
this->fee = fee;
|
this->fee = fee;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransactionBuilder::SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, libzcash::SaplingFullViewingKey fvkOut)
|
void TransactionBuilder::SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, uint256 ovk)
|
||||||
{
|
{
|
||||||
zChangeAddr = std::make_pair(fvkOut, changeAddr);
|
zChangeAddr = std::make_pair(ovk, changeAddr);
|
||||||
tChangeAddr = boost::none;
|
tChangeAddr = boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ boost::optional<CTransaction> TransactionBuilder::Build()
|
|||||||
auto fvk = spends[0].expsk.full_viewing_key();
|
auto fvk = spends[0].expsk.full_viewing_key();
|
||||||
auto note = spends[0].note;
|
auto note = spends[0].note;
|
||||||
libzcash::SaplingPaymentAddress changeAddr(note.d, note.pk_d);
|
libzcash::SaplingPaymentAddress changeAddr(note.d, note.pk_d);
|
||||||
AddSaplingOutput(fvk, changeAddr, change, {});
|
AddSaplingOutput(fvk.ovk, changeAddr, change, {});
|
||||||
} else {
|
} else {
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ private:
|
|||||||
std::vector<OutputDescriptionInfo> outputs;
|
std::vector<OutputDescriptionInfo> outputs;
|
||||||
std::vector<TransparentInputInfo> tIns;
|
std::vector<TransparentInputInfo> tIns;
|
||||||
|
|
||||||
boost::optional<std::pair<libzcash::SaplingFullViewingKey, libzcash::SaplingPaymentAddress>> zChangeAddr;
|
boost::optional<std::pair<uint256, libzcash::SaplingPaymentAddress>> zChangeAddr;
|
||||||
boost::optional<CTxDestination> tChangeAddr;
|
boost::optional<CTxDestination> tChangeAddr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -83,7 +83,7 @@ public:
|
|||||||
SaplingWitness witness);
|
SaplingWitness witness);
|
||||||
|
|
||||||
void AddSaplingOutput(
|
void AddSaplingOutput(
|
||||||
libzcash::SaplingFullViewingKey from,
|
uint256 ovk,
|
||||||
libzcash::SaplingPaymentAddress to,
|
libzcash::SaplingPaymentAddress to,
|
||||||
CAmount value,
|
CAmount value,
|
||||||
std::array<unsigned char, ZC_MEMO_SIZE> memo);
|
std::array<unsigned char, ZC_MEMO_SIZE> memo);
|
||||||
@@ -93,7 +93,7 @@ public:
|
|||||||
|
|
||||||
bool AddTransparentOutput(CTxDestination& to, CAmount value);
|
bool AddTransparentOutput(CTxDestination& to, CAmount value);
|
||||||
|
|
||||||
void SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, libzcash::SaplingFullViewingKey fvkOut);
|
void SendChangeTo(libzcash::SaplingPaymentAddress changeAddr, uint256 ovk);
|
||||||
|
|
||||||
bool SendChangeTo(CTxDestination& changeAddr);
|
bool SendChangeTo(CTxDestination& changeAddr);
|
||||||
|
|
||||||
|
|||||||
@@ -380,13 +380,23 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||||||
|
|
||||||
// Get various necessary keys
|
// Get various necessary keys
|
||||||
SaplingExpandedSpendingKey expsk;
|
SaplingExpandedSpendingKey expsk;
|
||||||
SaplingFullViewingKey from;
|
uint256 ovk;
|
||||||
if (isfromzaddr_) {
|
if (isfromzaddr_) {
|
||||||
auto sk = boost::get<libzcash::SaplingExtendedSpendingKey>(spendingkey_);
|
auto sk = boost::get<libzcash::SaplingExtendedSpendingKey>(spendingkey_);
|
||||||
expsk = sk.expsk;
|
expsk = sk.expsk;
|
||||||
from = expsk.full_viewing_key();
|
ovk = expsk.full_viewing_key().ovk;
|
||||||
} else {
|
} else {
|
||||||
// TODO: Set "from" to something!
|
// Sending from a t-address, which we don't have an ovk for. Instead,
|
||||||
|
// generate a common one from the HD seed. This ensures the data is
|
||||||
|
// recoverable, while keeping it logically separate from the ZIP 32
|
||||||
|
// Sapling key hierarchy, which the user might not be using.
|
||||||
|
HDSeed seed;
|
||||||
|
if (!pwalletMain->GetHDSeed(seed)) {
|
||||||
|
throw JSONRPCError(
|
||||||
|
RPC_WALLET_ERROR,
|
||||||
|
"CWallet::GenerateNewSaplingZKey(): HD seed not found");
|
||||||
|
}
|
||||||
|
ovk = ovkForShieldingFromTaddr(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set change address if we are using transparent funds
|
// Set change address if we are using transparent funds
|
||||||
@@ -450,7 +460,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||||||
|
|
||||||
auto memo = get_memo_from_hex_string(hexMemo);
|
auto memo = get_memo_from_hex_string(hexMemo);
|
||||||
|
|
||||||
builder_.AddSaplingOutput(from, to, value, memo);
|
builder_.AddSaplingOutput(ovk, to, value, memo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add transparent outputs
|
// Add transparent outputs
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) {
|
|||||||
|
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder.AddSaplingOutput(fvk, pk, 50000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 50000, {});
|
||||||
builder.SetFee(0);
|
builder.SetFee(0);
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
@@ -504,7 +504,7 @@ TEST(WalletTests, FindMySaplingNotes) {
|
|||||||
// Generate transaction
|
// Generate transaction
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder.AddSaplingOutput(fvk, pk, 25000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
auto tx = maybe_tx.get();
|
auto tx = maybe_tx.get();
|
||||||
@@ -644,7 +644,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
|
|||||||
// Generate tx to create output note B
|
// Generate tx to create output note B
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder.AddSaplingOutput(fvk, pk, 35000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 35000, {});
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
auto tx = maybe_tx.get();
|
auto tx = maybe_tx.get();
|
||||||
@@ -700,7 +700,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
|
|||||||
// Create transaction to spend note B
|
// Create transaction to spend note B
|
||||||
auto builder2 = TransactionBuilder(consensusParams, 2);
|
auto builder2 = TransactionBuilder(consensusParams, 2);
|
||||||
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
|
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
|
||||||
builder2.AddSaplingOutput(fvk, pk, 20000, {});
|
builder2.AddSaplingOutput(fvk.ovk, pk, 20000, {});
|
||||||
auto maybe_tx2 = builder2.Build();
|
auto maybe_tx2 = builder2.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
||||||
auto tx2 = maybe_tx2.get();
|
auto tx2 = maybe_tx2.get();
|
||||||
@@ -708,7 +708,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
|
|||||||
// Create conflicting transaction which also spends note B
|
// Create conflicting transaction which also spends note B
|
||||||
auto builder3 = TransactionBuilder(consensusParams, 2);
|
auto builder3 = TransactionBuilder(consensusParams, 2);
|
||||||
ASSERT_TRUE(builder3.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
|
ASSERT_TRUE(builder3.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
|
||||||
builder3.AddSaplingOutput(fvk, pk, 19999, {});
|
builder3.AddSaplingOutput(fvk.ovk, pk, 19999, {});
|
||||||
auto maybe_tx3 = builder3.Build();
|
auto maybe_tx3 = builder3.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx3), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx3), true);
|
||||||
auto tx3 = maybe_tx3.get();
|
auto tx3 = maybe_tx3.get();
|
||||||
@@ -809,7 +809,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) {
|
|||||||
// Generate transaction
|
// Generate transaction
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder.AddSaplingOutput(fvk, pk, 25000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
auto tx = maybe_tx.get();
|
auto tx = maybe_tx.get();
|
||||||
@@ -906,7 +906,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
|
|||||||
// Generate transaction
|
// Generate transaction
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder.AddSaplingOutput(fvk, pk, 25000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
auto tx = maybe_tx.get();
|
auto tx = maybe_tx.get();
|
||||||
@@ -1042,7 +1042,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
|
|||||||
// Generate transaction, which sends funds to note B
|
// Generate transaction, which sends funds to note B
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder.AddSaplingOutput(fvk, pk, 25000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
auto tx = maybe_tx.get();
|
auto tx = maybe_tx.get();
|
||||||
@@ -1114,7 +1114,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
|
|||||||
// Create transaction to spend note B
|
// Create transaction to spend note B
|
||||||
auto builder2 = TransactionBuilder(consensusParams, 2);
|
auto builder2 = TransactionBuilder(consensusParams, 2);
|
||||||
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
|
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note2, anchor, spend_note_witness));
|
||||||
builder2.AddSaplingOutput(fvk, pk, 12500, {});
|
builder2.AddSaplingOutput(fvk.ovk, pk, 12500, {});
|
||||||
auto maybe_tx2 = builder2.Build();
|
auto maybe_tx2 = builder2.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
||||||
auto tx2 = maybe_tx2.get();
|
auto tx2 = maybe_tx2.get();
|
||||||
@@ -1744,7 +1744,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
|
|||||||
// Generate transaction
|
// Generate transaction
|
||||||
auto builder = TransactionBuilder(consensusParams, 1);
|
auto builder = TransactionBuilder(consensusParams, 1);
|
||||||
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder.AddSaplingOutput(fvk, pk2, 25000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk2, 25000, {});
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
auto tx = maybe_tx.get();
|
auto tx = maybe_tx.get();
|
||||||
@@ -1894,7 +1894,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
|
|||||||
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
|
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
|
||||||
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
|
||||||
builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
|
builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
|
||||||
builder.AddSaplingOutput(fvk, pk, 40000, {});
|
builder.AddSaplingOutput(fvk.ovk, pk, 40000, {});
|
||||||
auto maybe_tx = builder.Build();
|
auto maybe_tx = builder.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
|
||||||
auto tx1 = maybe_tx.get();
|
auto tx1 = maybe_tx.get();
|
||||||
@@ -1951,7 +1951,7 @@ TEST(WalletTests, MarkAffectedSaplingTransactionsDirty) {
|
|||||||
// 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change
|
// 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change
|
||||||
auto builder2 = TransactionBuilder(consensusParams, 2);
|
auto builder2 = TransactionBuilder(consensusParams, 2);
|
||||||
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note, anchor, witness));
|
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note, anchor, witness));
|
||||||
builder2.AddSaplingOutput(fvk, pk, 25000, {});
|
builder2.AddSaplingOutput(fvk.ovk, pk, 25000, {});
|
||||||
auto maybe_tx2 = builder2.Build();
|
auto maybe_tx2 = builder2.Build();
|
||||||
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
|
||||||
auto tx2 = maybe_tx2.get();
|
auto tx2 = maybe_tx2.get();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "streams.h"
|
#include "streams.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "zcash/prf.h"
|
||||||
|
|
||||||
#include <librustzcash.h>
|
#include <librustzcash.h>
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
@@ -15,6 +16,9 @@
|
|||||||
const unsigned char ZCASH_HD_SEED_FP_PERSONAL[crypto_generichash_blake2b_PERSONALBYTES] =
|
const unsigned char ZCASH_HD_SEED_FP_PERSONAL[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||||
{'Z', 'c', 'a', 's', 'h', '_', 'H', 'D', '_', 'S', 'e', 'e', 'd', '_', 'F', 'P'};
|
{'Z', 'c', 'a', 's', 'h', '_', 'H', 'D', '_', 'S', 'e', 'e', 'd', '_', 'F', 'P'};
|
||||||
|
|
||||||
|
const unsigned char ZCASH_TADDR_OVK_PERSONAL[crypto_generichash_blake2b_PERSONALBYTES] =
|
||||||
|
{'Z', 'c', 'T', 'a', 'd', 'd', 'r', 'T', 'o', 'S', 'a', 'p', 'l', 'i', 'n', 'g'};
|
||||||
|
|
||||||
HDSeed HDSeed::Random(size_t len)
|
HDSeed HDSeed::Random(size_t len)
|
||||||
{
|
{
|
||||||
assert(len >= 32);
|
assert(len >= 32);
|
||||||
@@ -30,6 +34,29 @@ uint256 HDSeed::Fingerprint() const
|
|||||||
return h.GetHash();
|
return h.GetHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint256 ovkForShieldingFromTaddr(HDSeed& seed) {
|
||||||
|
auto rawSeed = seed.RawSeed();
|
||||||
|
|
||||||
|
// I = BLAKE2b-512("ZcTaddrToSapling", seed)
|
||||||
|
crypto_generichash_blake2b_state state;
|
||||||
|
assert(crypto_generichash_blake2b_init_salt_personal(
|
||||||
|
&state,
|
||||||
|
NULL, 0, // No key.
|
||||||
|
64,
|
||||||
|
NULL, // No salt.
|
||||||
|
ZCASH_TADDR_OVK_PERSONAL) == 0);
|
||||||
|
crypto_generichash_blake2b_update(&state, rawSeed.data(), rawSeed.size());
|
||||||
|
auto intermediate = std::array<unsigned char, 64>();
|
||||||
|
crypto_generichash_blake2b_final(&state, intermediate.data(), 64);
|
||||||
|
|
||||||
|
// I_L = I[0..32]
|
||||||
|
uint256 intermediate_L;
|
||||||
|
memcpy(intermediate_L.begin(), intermediate.data(), 32);
|
||||||
|
|
||||||
|
// ovk = truncate_32(PRF^expand(I_L, [0x02]))
|
||||||
|
return PRF_ovk(intermediate_L);
|
||||||
|
}
|
||||||
|
|
||||||
namespace libzcash {
|
namespace libzcash {
|
||||||
|
|
||||||
boost::optional<SaplingExtendedFullViewingKey> SaplingExtendedFullViewingKey::Derive(uint32_t i) const
|
boost::optional<SaplingExtendedFullViewingKey> SaplingExtendedFullViewingKey::Derive(uint32_t i) const
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is not part of ZIP 32, but is here because it's linked to the HD seed.
|
||||||
|
uint256 ovkForShieldingFromTaddr(HDSeed& seed);
|
||||||
|
|
||||||
namespace libzcash {
|
namespace libzcash {
|
||||||
|
|
||||||
typedef blob88 diversifier_index_t;
|
typedef blob88 diversifier_index_t;
|
||||||
|
|||||||
Reference in New Issue
Block a user