Latest Zcash updates

This commit is contained in:
miketout
2018-09-20 13:32:57 -07:00
38 changed files with 996 additions and 515 deletions

View File

@@ -82,6 +82,15 @@ uint32_t CurrentEpochBranchId(int nHeight, const Consensus::Params& params) {
return NetworkUpgradeInfo[CurrentEpoch(nHeight, params)].nBranchId;
}
bool IsConsensusBranchId(int branchId) {
for (int idx = Consensus::BASE_SPROUT; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) {
if (branchId == NetworkUpgradeInfo[idx].nBranchId) {
return true;
}
}
return false;
}
bool IsActivationHeight(
int nHeight,
const Consensus::Params& params,
@@ -114,20 +123,28 @@ bool IsActivationHeightForAnyUpgrade(
return false;
}
boost::optional<int> NextActivationHeight(
int nHeight,
const Consensus::Params& params)
{
boost::optional<int> NextEpoch(int nHeight, const Consensus::Params& params) {
if (nHeight < 0) {
return boost::none;
}
// Don't count Sprout as an activation height
// Sprout is never pending
for (auto idx = Consensus::BASE_SPROUT + 1; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) {
if (NetworkUpgradeState(nHeight, params, Consensus::UpgradeIndex(idx)) == UPGRADE_PENDING) {
return params.vUpgrades[idx].nActivationHeight;
return idx;
}
}
return boost::none;
}
boost::optional<int> NextActivationHeight(
int nHeight,
const Consensus::Params& params)
{
auto idx = NextEpoch(nHeight, params);
if (idx) {
return params.vUpgrades[idx.get()].nActivationHeight;
}
return boost::none;
}

View File

@@ -63,6 +63,12 @@ int CurrentEpoch(int nHeight, const Consensus::Params& params);
*/
uint32_t CurrentEpochBranchId(int nHeight, const Consensus::Params& params);
/**
* Returns true if a given branch id is a valid nBranchId for one of the network
* upgrades contained in NetworkUpgradeInfo.
*/
bool IsConsensusBranchId(int branchId);
/**
* Returns true if the given block height is the activation height for the given
* upgrade.
@@ -79,6 +85,12 @@ bool IsActivationHeightForAnyUpgrade(
int nHeight,
const Consensus::Params& params);
/**
* Returns the index of the next upgrade after the given block height, or
* boost::none if there are no more known upgrades.
*/
boost::optional<int> NextEpoch(int nHeight, const Consensus::Params& params);
/**
* Returns the activation height for the next upgrade after the given block height,
* or boost::none if there are no more known upgrades.

View File

@@ -506,8 +506,11 @@ TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) {
CTransaction tx(mtx);
MockCValidationState state;
// during initial block download, DoS ban score should be zero, else 100
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
ContextualCheckTransaction(tx, state, 0, 100);
ContextualCheckTransaction(tx, state, 0, 100, []() { return true; });
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
ContextualCheckTransaction(tx, state, 0, 100, []() { return false; });
}
TEST(checktransaction_tests, non_canonical_ed25519_signature) {
@@ -539,8 +542,11 @@ TEST(checktransaction_tests, non_canonical_ed25519_signature) {
CTransaction tx(mtx);
MockCValidationState state;
// during initial block download, DoS ban score should be zero, else 100
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
ContextualCheckTransaction(tx, state, 0, 100);
ContextualCheckTransaction(tx, state, 0, 100, []() { return true; });
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
ContextualCheckTransaction(tx, state, 0, 100, []() { return false; });
}
TEST(checktransaction_tests, OverwinterConstructors) {
@@ -829,8 +835,11 @@ TEST(checktransaction_tests, OverwinterNotActive) {
CTransaction tx(mtx);
MockCValidationState state;
// during initial block download, DoS ban score should be zero, else 100
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
ContextualCheckTransaction(tx, state, 1, 100);
ContextualCheckTransaction(tx, state, 1, 100, []() { return true; });
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
ContextualCheckTransaction(tx, state, 1, 100, []() { return false; });
}
// This tests a transaction without the fOverwintered flag set, against the Overwinter consensus rule set.

View File

@@ -38,7 +38,7 @@ TEST(TransactionBuilder, Invoke)
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
auto builder1 = TransactionBuilder(consensusParams, 1, &keystore);
builder1.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
builder1.AddSaplingOutput(fvk_from, pk, 40000, {});
builder1.AddSaplingOutput(fvk_from.ovk, pk, 40000, {});
auto maybe_tx1 = builder1.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx1), true);
auto tx1 = maybe_tx1.get();
@@ -73,7 +73,7 @@ TEST(TransactionBuilder, Invoke)
// Check that trying to add a different anchor fails
ASSERT_FALSE(builder2.AddSaplingSpend(expsk, note, uint256(), witness));
builder2.AddSaplingOutput(fvk, pk, 25000, {});
builder2.AddSaplingOutput(fvk.ovk, pk, 25000, {});
auto maybe_tx2 = builder2.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
auto tx2 = maybe_tx2.get();
@@ -153,7 +153,7 @@ TEST(TransactionBuilder, FailsWithNegativeChange)
// Fail if there is only a Sapling output
// 0.0005 z-ZEC out, 0.0001 t-ZEC fee
auto builder = TransactionBuilder(consensusParams, 1);
builder.AddSaplingOutput(fvk, pk, 50000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 50000, {});
EXPECT_FALSE(static_cast<bool>(builder.Build()));
// Fail if there is only a transparent output
@@ -237,7 +237,7 @@ TEST(TransactionBuilder, ChangeOutput)
{
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
builder.SendChangeTo(zChangeAddr, fvkOut);
builder.SendChangeTo(zChangeAddr, fvkOut.ovk);
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
@@ -298,7 +298,7 @@ TEST(TransactionBuilder, SetFee)
{
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 25000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
@@ -315,7 +315,7 @@ TEST(TransactionBuilder, SetFee)
{
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 25000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
builder.SetFee(20000);
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);

View File

@@ -143,6 +143,35 @@ TEST_F(UpgradesTest, IsActivationHeightForAnyUpgrade) {
EXPECT_FALSE(IsActivationHeightForAnyUpgrade(1000000, params));
}
TEST_F(UpgradesTest, NextEpoch) {
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus();
// Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT
EXPECT_EQ(NextEpoch(-1, params), boost::none);
EXPECT_EQ(NextEpoch(0, params), boost::none);
EXPECT_EQ(NextEpoch(1, params), boost::none);
EXPECT_EQ(NextEpoch(1000000, params), boost::none);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
EXPECT_EQ(NextEpoch(-1, params), boost::none);
EXPECT_EQ(NextEpoch(0, params), boost::none);
EXPECT_EQ(NextEpoch(1, params), boost::none);
EXPECT_EQ(NextEpoch(1000000, params), boost::none);
int nActivationHeight = 100;
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, nActivationHeight);
EXPECT_EQ(NextEpoch(-1, params), boost::none);
EXPECT_EQ(NextEpoch(0, params), static_cast<int>(Consensus::UPGRADE_TESTDUMMY));
EXPECT_EQ(NextEpoch(1, params), static_cast<int>(Consensus::UPGRADE_TESTDUMMY));
EXPECT_EQ(NextEpoch(nActivationHeight - 1, params), static_cast<int>(Consensus::UPGRADE_TESTDUMMY));
EXPECT_EQ(NextEpoch(nActivationHeight, params), boost::none);
EXPECT_EQ(NextEpoch(nActivationHeight + 1, params), boost::none);
EXPECT_EQ(NextEpoch(1000000, params), boost::none);
}
TEST_F(UpgradesTest, NextActivationHeight) {
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& params = Params().GetConsensus();

View File

@@ -226,3 +226,13 @@ bool CBasicKeyStore::GetSaplingIncomingViewingKey(const libzcash::SaplingPayment
}
return false;
}
bool CBasicKeyStore::GetSaplingExtendedSpendingKey(const libzcash::SaplingPaymentAddress &addr,
libzcash::SaplingExtendedSpendingKey &extskOut) const {
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
return GetSaplingIncomingViewingKey(addr, ivk) &&
GetSaplingFullViewingKey(ivk, fvk) &&
GetSaplingSpendingKey(fvk, extskOut);
}

View File

@@ -274,6 +274,11 @@ public:
virtual bool GetSaplingIncomingViewingKey(
const libzcash::SaplingPaymentAddress &addr,
libzcash::SaplingIncomingViewingKey& ivkOut) const;
bool GetSaplingExtendedSpendingKey(
const libzcash::SaplingPaymentAddress &addr,
libzcash::SaplingExtendedSpendingKey &extskOut) const;
void GetSaplingPaymentAddresses(std::set<libzcash::SaplingPaymentAddress> &setAddress) const
{
setAddress.clear();

View File

@@ -1504,7 +1504,7 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height)
}
if (newPOSActive && !(validHash && posHash <= target))
{
printf("ERROR: invalid nonce value for PoS block\nnNonce: %s\nrawHash: %s\nposHash: %s\nvalue: %ull\n",
printf("ERROR: invalid nonce value for PoS block\nnNonce: %s\nrawHash: %s\nposHash: %s\nvalue: %lu\n",
pblock->nNonce.GetHex().c_str(), rawHash.GetHex().c_str(), posHash.GetHex().c_str(), value);
}
//else

View File

@@ -305,8 +305,7 @@ namespace {
int GetHeight()
{
LOCK(cs_main);
return chainActive.Height();
return chainActive.LastTip()->nHeight;
}
void UpdatePreferredDownload(CNode* node, CNodeState* state)
@@ -999,8 +998,14 @@ bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeigh
* 1. AcceptToMemoryPool calls CheckTransaction and this function.
* 2. ProcessNewBlock calls AcceptBlock, which calls CheckBlock (which calls CheckTransaction)
* and ContextualCheckBlock (which calls this function).
* 3. The isInitBlockDownload argument is only to assist with testing.
*/
bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, const int nHeight, const int dosLevel)
bool ContextualCheckTransaction(
const CTransaction& tx,
CValidationState &state,
const int nHeight,
const int dosLevel,
bool (*isInitBlockDownload)())
{
bool overwinterActive = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER);
bool saplingActive = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING);
@@ -1008,7 +1013,7 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
// If Sprout rules apply, reject transactions which are intended for Overwinter and beyond
if (isSprout && tx.fOverwintered) {
return state.DoS(IsInitialBlockDownload() ? 0 : dosLevel,
return state.DoS(isInitBlockDownload() ? 0 : dosLevel,
error("ContextualCheckTransaction(): overwinter is not active yet"),
REJECT_INVALID, "tx-overwinter-not-active");
}
@@ -1111,7 +1116,7 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state,
dataToBeSigned.begin(), 32,
tx.joinSplitPubKey.begin()
) != 0) {
return state.DoS(IsInitialBlockDownload() ? 0 : 100,
return state.DoS(isInitBlockDownload() ? 0 : 100,
error("CheckTransaction(): invalid joinsplit signature"),
REJECT_INVALID, "bad-txns-invalid-joinsplit-signature");
}
@@ -6302,16 +6307,16 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
pfrom->fDisconnect = true;
return false;
}
// When Overwinter is active, reject incoming connections from non-Overwinter nodes
// Reject incoming connections from nodes that don't know about the current epoch
const Consensus::Params& params = Params().GetConsensus();
if (NetworkUpgradeActive(GetHeight(), params, Consensus::UPGRADE_OVERWINTER)
&& nVersion < params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)
auto currentEpoch = CurrentEpoch(GetHeight(), params);
if (pfrom->nVersion < params.vUpgrades[currentEpoch].nProtocolVersion)
{
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater",
params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion));
strprintf("Version must be %d or greater",
params.vUpgrades[currentEpoch].nProtocolVersion));
pfrom->fDisconnect = true;
return false;
}
@@ -6438,15 +6443,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// Disconnect existing peer connection when:
// 1. The version message has been received
// 2. Overwinter is active
// 3. Peer version is pre-Overwinter
else if (NetworkUpgradeActive(GetHeight(), chainparams.GetConsensus(), Consensus::UPGRADE_OVERWINTER)
&& (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion))
// 2. Peer version is below the minimum version for the current epoch
else if (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[
CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion)
{
LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater",
chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion));
strprintf("Version must be %d or greater",
chainparams.GetConsensus().vUpgrades[
CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion));
pfrom->fDisconnect = true;
return false;
}

View File

@@ -689,7 +689,8 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons
std::vector<CScriptCheck> *pvChecks = NULL);
/** Check a transaction contextually against a set of consensus rules */
bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel);
bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel,
bool (*isInitBlockDownload)() = IsInitialBlockDownload);
/** Apply the effects of this transaction on the UTXO set represented by view */
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight);

View File

@@ -824,22 +824,26 @@ static bool AttemptToEvictConnection(bool fPreferNewConnection) {
}
const Consensus::Params& params = Params().GetConsensus();
int nActivationHeight = params.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight;
auto nextEpoch = NextEpoch(height, params);
if (nextEpoch) {
auto idx = nextEpoch.get();
int nActivationHeight = params.vUpgrades[idx].nActivationHeight;
if (nActivationHeight > 0 &&
height < nActivationHeight &&
height >= nActivationHeight - NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD)
{
// Find any nodes which don't support Overwinter protocol version
BOOST_FOREACH(const CNodeRef &node, vEvictionCandidates) {
if (node->nVersion < params.vUpgrades[Consensus::UPGRADE_SAPLING].nProtocolVersion) {
vTmpEvictionCandidates.push_back(node);
if (nActivationHeight > 0 &&
height < nActivationHeight &&
height >= nActivationHeight - NETWORK_UPGRADE_PEER_PREFERENCE_BLOCK_PERIOD)
{
// Find any nodes which don't support the protocol version for the next upgrade
for (const CNodeRef &node : vEvictionCandidates) {
if (node->nVersion < params.vUpgrades[idx].nProtocolVersion) {
vTmpEvictionCandidates.push_back(node);
}
}
}
// Prioritize these nodes by replacing eviction set with them
if (vTmpEvictionCandidates.size() > 0) {
vEvictionCandidates = vTmpEvictionCandidates;
// Prioritize these nodes by replacing eviction set with them
if (vTmpEvictionCandidates.size() > 0) {
vEvictionCandidates = vTmpEvictionCandidates;
}
}
}

View File

@@ -7,6 +7,7 @@
#include "consensus/validation.h"
#include "core_io.h"
#include "init.h"
#include "deprecation.h"
#include "key_io.h"
#include "keystore.h"
#include "main.h"
@@ -122,6 +123,36 @@ UniValue TxJoinSplitToJSON(const CTransaction& tx) {
uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight);
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;
}
int32_t myIsutxo_spent(uint256 &spenttxid,uint256 txid,int32_t vout)
{
CSpentIndexValue spentInfo; CSpentIndexKey spentKey(txid,vout);
@@ -205,7 +236,6 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue&
interest = komodo_accrued_interest(&txheight,&locktime,tx.GetHash(),i,0,txout.nValue,(int32_t)tipindex->nHeight);
out.push_back(Pair("interest", ValueFromAmount(interest)));
}
out.push_back(Pair("valueZat", txout.nValue));
out.push_back(Pair("valueSat", txout.nValue)); // [+] Decker
out.push_back(Pair("n", (int64_t)i));
UniValue o(UniValue::VOBJ);
@@ -228,6 +258,17 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue&
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()));
@@ -246,12 +287,16 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue&
void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
{
uint256 txid = tx.GetHash();
entry.push_back(Pair("txid", txid.GetHex()));
entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
entry.push_back(Pair("txid", tx.GetHash().GetHex()));
entry.push_back(Pair("overwintered", tx.fOverwintered));
entry.push_back(Pair("version", tx.nVersion));
if (tx.fOverwintered) {
entry.push_back(Pair("versiongroupid", HexInt(tx.nVersionGroupId)));
}
entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
if (tx.fOverwintered) {
entry.push_back(Pair("expiryheight", (int64_t)tx.nExpiryHeight));
}
UniValue vin(UniValue::VARR);
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
UniValue in(UniValue::VOBJ);
@@ -261,7 +306,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
in.push_back(Pair("vout", (int64_t)txin.prevout.n));
UniValue o(UniValue::VOBJ);
o.push_back(Pair("asm", txin.scriptSig.ToString()));
o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
in.push_back(Pair("scriptSig", o));
}
@@ -269,13 +314,21 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
vin.push_back(in);
}
entry.push_back(Pair("vin", vin));
UniValue vout(UniValue::VARR);
BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
CBlockIndex *tipindex,*pindex = it->second;
uint64_t interest;
for (unsigned int i = 0; i < tx.vout.size(); i++) {
const CTxOut& txout = tx.vout[i];
UniValue out(UniValue::VOBJ);
out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
out.push_back(Pair("valueSat", txout.nValue));
if ( ASSETCHAINS_SYMBOL[0] == 0 && pindex != 0 && tx.nLockTime >= 500000000 && (tipindex= chainActive.LastTip()) != 0 )
{
int64_t interest; int32_t txheight; uint32_t locktime;
interest = komodo_accrued_interest(&txheight,&locktime,tx.GetHash(),i,0,txout.nValue,(int32_t)tipindex->nHeight);
out.push_back(Pair("interest", ValueFromAmount(interest)));
}
out.push_back(Pair("valueZat", txout.nValue));
out.push_back(Pair("n", (int64_t)i));
UniValue o(UniValue::VOBJ);
ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
@@ -283,23 +336,33 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
vout.push_back(out);
}
entry.push_back(Pair("vout", vout));
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);
if (mi != mapBlockIndex.end() && (*mi).second) {
CBlockIndex* pindex = (*mi).second;
if (chainActive.Contains(pindex)) {
entry.push_back(Pair("height", pindex->nHeight));
entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight));
entry.push_back(Pair("time", pindex->GetBlockTime()));
entry.push_back(Pair("blocktime", pindex->GetBlockTime()));
} else {
entry.push_back(Pair("height", -1));
entry.push_back(Pair("confirmations", 0));
}
else
entry.push_back(Pair("confirmations", 0));
}
}
}
@@ -870,7 +933,7 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
UniValue signrawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 4)
if (fHelp || params.size() < 1 || params.size() > 5)
throw runtime_error(
"signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n"
"\nSign inputs for raw transaction (serialized, hex-encoded).\n"
@@ -907,6 +970,8 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\"\n"
"5. \"branchid\" (string, optional) The hex representation of the consensus branch id to sign with."
" This can be used to force signing with consensus rules that are ahead of the node's current height.\n"
"\nResult:\n"
"{\n"
@@ -934,7 +999,7 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
#else
LOCK(cs_main);
#endif
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true);
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR)(UniValue::VSTR), true);
vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
@@ -1070,10 +1135,20 @@ UniValue signrawtransaction(const UniValue& params, bool fHelp)
}
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
// Use the approximate release height if it is greater so offline nodes
// have a better estimation of the current height and will be more likely to
// determine the correct consensus branch ID.
int chainHeight = std::max(chainActive.Height() + 1, APPROX_RELEASE_HEIGHT);
// Grab the current consensus branch ID
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
auto consensusBranchId = CurrentEpochBranchId(chainHeight, Params().GetConsensus());
if (params.size() > 4 && !params[4].isNull()) {
consensusBranchId = ParseHexToUInt32(params[4].get_str());
if (!IsConsensusBranchId(consensusBranchId)) {
throw runtime_error(params[4].get_str() + " is not a valid consensus branch id");
}
}
// Script verification errors
UniValue vErrors(UniValue::VARR);

View File

@@ -92,6 +92,8 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams)
BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null NONE|ANYONECANPAY"));
BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" [] [] NONE|ANYONECANPAY"));
BOOST_CHECK_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null badenum"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" [] [] NONE|ANYONECANPAY 5ba81b19"));
BOOST_CHECK_THROW(CallRPC(string("signrawtransaction ")+rawtx+" [] [] ALL NONE|ANYONECANPAY 123abc"), runtime_error);
// Only check failure cases for sendrawtransaction, there's no network to send to...
BOOST_CHECK_THROW(CallRPC("sendrawtransaction"), runtime_error);

View File

@@ -1252,6 +1252,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.
*/

View File

@@ -47,13 +47,13 @@ bool TransactionBuilder::AddSaplingSpend(
}
void TransactionBuilder::AddSaplingOutput(
libzcash::SaplingFullViewingKey from,
uint256 ovk,
libzcash::SaplingPaymentAddress to,
CAmount value,
std::array<unsigned char, ZC_MEMO_SIZE> memo)
{
auto note = libzcash::SaplingNote(to, value);
outputs.emplace_back(from.ovk, note, memo);
outputs.emplace_back(ovk, note, memo);
mtx.valueBalance -= value;
}
@@ -84,9 +84,9 @@ void TransactionBuilder::SetFee(CAmount 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;
}
@@ -136,7 +136,7 @@ boost::optional<CTransaction> TransactionBuilder::Build()
auto fvk = spends[0].expsk.full_viewing_key();
auto note = spends[0].note;
libzcash::SaplingPaymentAddress changeAddr(note.d, note.pk_d);
AddSaplingOutput(fvk, changeAddr, change, {});
AddSaplingOutput(fvk.ovk, changeAddr, change, {});
} else {
return boost::none;
}

View File

@@ -65,7 +65,7 @@ private:
std::vector<OutputDescriptionInfo> outputs;
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;
public:
@@ -83,7 +83,7 @@ public:
SaplingWitness witness);
void AddSaplingOutput(
libzcash::SaplingFullViewingKey from,
uint256 ovk,
libzcash::SaplingPaymentAddress to,
CAmount value,
std::array<unsigned char, ZC_MEMO_SIZE> memo);
@@ -93,7 +93,7 @@ public:
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);

View File

@@ -385,13 +385,23 @@ bool AsyncRPCOperation_sendmany::main_impl() {
// Get various necessary keys
SaplingExpandedSpendingKey expsk;
SaplingFullViewingKey from;
uint256 ovk;
if (isfromzaddr_) {
auto sk = boost::get<libzcash::SaplingExtendedSpendingKey>(spendingkey_);
expsk = sk.expsk;
from = expsk.full_viewing_key();
ovk = expsk.full_viewing_key().ovk;
} 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
@@ -455,7 +465,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
auto memo = get_memo_from_hex_string(hexMemo);
builder_.AddSaplingOutput(from, to, value, memo);
builder_.AddSaplingOutput(ovk, to, value, memo);
}
// Add transparent outputs

View File

@@ -384,7 +384,7 @@ TEST(WalletTests, SetSaplingNoteAddrsInCWalletTx) {
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 50000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 50000, {});
builder.SetFee(0);
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
@@ -504,7 +504,7 @@ TEST(WalletTests, FindMySaplingNotes) {
// Generate transaction
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 25000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
@@ -644,7 +644,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
// Generate tx to create output note B
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 35000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 35000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
@@ -700,7 +700,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
// Create transaction to spend note B
auto builder2 = TransactionBuilder(consensusParams, 2);
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();
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
auto tx2 = maybe_tx2.get();
@@ -708,7 +708,7 @@ TEST(WalletTests, GetConflictedSaplingNotes) {
// Create conflicting transaction which also spends note B
auto builder3 = TransactionBuilder(consensusParams, 2);
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();
ASSERT_EQ(static_cast<bool>(maybe_tx3), true);
auto tx3 = maybe_tx3.get();
@@ -809,7 +809,7 @@ TEST(WalletTests, SaplingNullifierIsSpent) {
// Generate transaction
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 25000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
@@ -906,7 +906,7 @@ TEST(WalletTests, NavigateFromSaplingNullifierToNote) {
// Generate transaction
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 25000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
@@ -1042,7 +1042,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
// Generate transaction, which sends funds to note B
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 25000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 25000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
@@ -1114,7 +1114,7 @@ TEST(WalletTests, SpentSaplingNoteIsFromMe) {
// Create transaction to spend note B
auto builder2 = TransactionBuilder(consensusParams, 2);
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();
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
auto tx2 = maybe_tx2.get();
@@ -1744,7 +1744,7 @@ TEST(WalletTests, UpdatedSaplingNoteData) {
// Generate transaction
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk2, 25000, {});
builder.AddSaplingOutput(fvk.ovk, pk2, 25000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
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
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
builder.AddSaplingOutput(fvk, pk, 40000, {});
builder.AddSaplingOutput(fvk.ovk, pk, 40000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
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
auto builder2 = TransactionBuilder(consensusParams, 2);
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note, anchor, witness));
builder2.AddSaplingOutput(fvk, pk, 25000, {});
builder2.AddSaplingOutput(fvk.ovk, pk, 25000, {});
auto maybe_tx2 = builder2.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
auto tx2 = maybe_tx2.get();

View File

@@ -63,7 +63,7 @@ TEST(wallet_zkeys_tests, store_and_load_sapling_zkeys) {
/**
* This test covers methods on CWallet
* GenerateNewZKey()
* AddZKey()
* AddSproutZKey()
* LoadZKey()
* LoadZKeyMetadata()
*/
@@ -89,7 +89,7 @@ TEST(wallet_zkeys_tests, store_and_load_zkeys) {
// manually add new spending key to wallet
auto sk = libzcash::SproutSpendingKey::random();
ASSERT_TRUE(wallet.AddZKey(sk));
ASSERT_TRUE(wallet.AddSproutZKey(sk));
// verify wallet did add it
addr = sk.address();
@@ -116,7 +116,7 @@ TEST(wallet_zkeys_tests, store_and_load_zkeys) {
ASSERT_TRUE(wallet.LoadZKeyMetadata(addr, meta));
// check metadata is the same
CKeyMetadata m= wallet.mapZKeyMetadata[addr];
CKeyMetadata m= wallet.mapSproutZKeyMetadata[addr];
ASSERT_EQ(m.nCreateTime, now);
}
@@ -215,7 +215,7 @@ TEST(wallet_zkeys_tests, write_zkey_direct_to_db) {
ASSERT_EQ(1, addrs.size());
// wallet should have default metadata for addr with null createtime
CKeyMetadata m = wallet.mapZKeyMetadata[addr];
CKeyMetadata m = wallet.mapSproutZKeyMetadata[addr];
ASSERT_EQ(m.nCreateTime, 0);
ASSERT_NE(m.nCreateTime, now);
@@ -235,7 +235,7 @@ TEST(wallet_zkeys_tests, write_zkey_direct_to_db) {
ASSERT_EQ(2, addrs.size());
// check metadata is now the same
m = wallet.mapZKeyMetadata[addr];
m = wallet.mapSproutZKeyMetadata[addr];
ASSERT_EQ(m.nCreateTime, now);
}

View File

@@ -297,28 +297,23 @@ UniValue importwallet_impl(const UniValue& params, bool fHelp, bool fImportZKeys
// Let's see if the address is a valid Zcash spending key
if (fImportZKeys) {
auto spendingkey = DecodeSpendingKey(vstr[0]);
int64_t nTime = DecodeDumpTime(vstr[1]);
// Only include hdKeypath and seedFpStr if we have both
boost::optional<std::string> hdKeypath = (vstr.size() > 3) ? boost::optional<std::string>(vstr[2]) : boost::none;
boost::optional<std::string> seedFpStr = (vstr.size() > 3) ? boost::optional<std::string>(vstr[3]) : boost::none;
if (IsValidSpendingKey(spendingkey)) {
// TODO: Add Sapling support. For now, ensure we can freely convert.
assert(boost::get<libzcash::SproutSpendingKey>(&spendingkey) != nullptr);
auto key = boost::get<libzcash::SproutSpendingKey>(spendingkey);
auto addr = key.address();
if (pwalletMain->HaveSproutSpendingKey(addr)) {
LogPrint("zrpc", "Skipping import of zaddr %s (key already present)\n", EncodePaymentAddress(addr));
continue;
}
int64_t nTime = DecodeDumpTime(vstr[1]);
LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(addr));
if (!pwalletMain->AddZKey(key)) {
auto addResult = boost::apply_visitor(
AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus(), nTime, hdKeypath, seedFpStr, true), spendingkey);
if (addResult == KeyAlreadyExists){
LogPrint("zrpc", "Skipping import of zaddr (key already present)\n");
} else if (addResult == KeyNotAdded) {
// Something went wrong
fGood = false;
continue;
}
// Successfully imported zaddr. Now import the metadata.
pwalletMain->mapZKeyMetadata[addr].nCreateTime = nTime;
continue;
} else {
LogPrint("zrpc", "Importing detected an error: invalid spending key. Trying as a transparent key...\n");
// Not a valid spending key, so carry on and see if it's a Zcash style address.
// Not a valid spending key, so carry on and see if it's a Zcash style t-address.
}
}
@@ -508,8 +503,14 @@ UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys)
// produce output
file << strprintf("# Wallet dump created by Komodo %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.LastTip()->GetBlockHash().ToString());
file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.LastTip()->GetBlockTime()));
file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
file << strprintf("# mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
{
HDSeed hdSeed;
pwalletMain->GetHDSeed(hdSeed);
file << strprintf("# HDSeed=%s fingerprint=%s", pwalletMain->GetHDChain().seedFp.GetHex(), hdSeed.Fingerprint().GetHex());
file << "\n";
}
file << "\n";
for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
const CKeyID &keyid = it->second;
@@ -529,18 +530,37 @@ UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys)
file << "\n";
if (fDumpZKeys) {
std::set<libzcash::SproutPaymentAddress> addresses;
pwalletMain->GetSproutPaymentAddresses(addresses);
std::set<libzcash::SproutPaymentAddress> sproutAddresses;
pwalletMain->GetSproutPaymentAddresses(sproutAddresses);
file << "\n";
file << "# Zkeys\n";
file << "\n";
for (auto addr : addresses ) {
for (auto addr : sproutAddresses) {
libzcash::SproutSpendingKey key;
if (pwalletMain->GetSproutSpendingKey(addr, key)) {
std::string strTime = EncodeDumpTime(pwalletMain->mapZKeyMetadata[addr].nCreateTime);
std::string strTime = EncodeDumpTime(pwalletMain->mapSproutZKeyMetadata[addr].nCreateTime);
file << strprintf("%s %s # zaddr=%s\n", EncodeSpendingKey(key), strTime, EncodePaymentAddress(addr));
}
}
std::set<libzcash::SaplingPaymentAddress> saplingAddresses;
pwalletMain->GetSaplingPaymentAddresses(saplingAddresses);
file << "\n";
file << "# Sapling keys\n";
file << "\n";
for (auto addr : saplingAddresses) {
libzcash::SaplingExtendedSpendingKey extsk;
if (pwalletMain->GetSaplingExtendedSpendingKey(addr, extsk)) {
auto ivk = extsk.expsk.full_viewing_key().in_viewing_key();
CKeyMetadata keyMeta = pwalletMain->mapSaplingZKeyMetadata[ivk];
std::string strTime = EncodeDumpTime(keyMeta.nCreateTime);
// Keys imported with z_importkey do not have zip32 metadata
if (keyMeta.hdKeypath.empty() || keyMeta.seedFp.IsNull()) {
file << strprintf("%s %s # zaddr=%s\n", EncodeSpendingKey(extsk), strTime, EncodePaymentAddress(addr));
} else {
file << strprintf("%s %s %s %s # zaddr=%s\n", EncodeSpendingKey(extsk), strTime, keyMeta.hdKeypath, keyMeta.seedFp.GetHex(), EncodePaymentAddress(addr));
}
}
}
file << "\n";
}
@@ -550,67 +570,6 @@ UniValue dumpwallet_impl(const UniValue& params, bool fHelp, bool fDumpZKeys)
return exportfilepath.string();
}
class AddSpendingKeyToWallet : public boost::static_visitor<bool>
{
private:
CWallet *m_wallet;
const Consensus::Params &params;
public:
AddSpendingKeyToWallet(CWallet *wallet, const Consensus::Params &params) :
m_wallet(wallet), params(params) {}
bool operator()(const libzcash::SproutSpendingKey &sk) const {
auto addr = sk.address();
// Don't throw error in case a key is already there
if (m_wallet->HaveSproutSpendingKey(addr)) {
return true;
} else {
m_wallet->MarkDirty();
if (!m_wallet-> AddZKey(sk)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet");
}
m_wallet->mapZKeyMetadata[addr].nCreateTime = 1;
return false;
}
}
bool operator()(const libzcash::SaplingExtendedSpendingKey &sk) const {
auto fvk = sk.expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto addr = sk.DefaultAddress();
{
// Don't throw error in case a key is already there
if (m_wallet->HaveSaplingSpendingKey(fvk)) {
return true;
} else {
m_wallet->MarkDirty();
if (!m_wallet-> AddSaplingZKey(sk, addr)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet");
}
// Sapling addresses can't have been used in transactions prior to activation.
if (params.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight == Consensus::NetworkUpgrade::ALWAYS_ACTIVE) {
m_wallet->mapSaplingZKeyMetadata[ivk].nCreateTime = 1;
} else {
// Friday, 26 October 2018 00:00:00 GMT - definitely before Sapling activates
m_wallet->mapSaplingZKeyMetadata[ivk].nCreateTime = 1540512000;
}
return false;
}
}
}
bool operator()(const libzcash::InvalidEncoding& no) const {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key");
}
};
UniValue z_importkey(const UniValue& params, bool fHelp)
{
@@ -683,11 +642,14 @@ UniValue z_importkey(const UniValue& params, bool fHelp)
}
// Sapling support
auto keyAlreadyExists = boost::apply_visitor(
AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey);
if (keyAlreadyExists && fIgnoreExistingKey) {
auto addResult = boost::apply_visitor(AddSpendingKeyToWallet(pwalletMain, Params().GetConsensus()), spendingkey);
if (addResult == KeyAlreadyExists && fIgnoreExistingKey) {
return NullUniValue;
}
pwalletMain->MarkDirty();
if (addResult == KeyNotAdded) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spending key to wallet");
}
// whenever a key is imported, we need to scan the whole chain
pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'

View File

@@ -2574,7 +2574,8 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
" \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n"
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in KMD/KB\n"
" \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
" \"seedfp\": \"uint256\", (string) the BLAKE2b-256 hash of the HD seed\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getwalletinfo", "")
@@ -2594,6 +2595,9 @@ UniValue getwalletinfo(const UniValue& params, bool fHelp)
if (pwalletMain->IsCrypted())
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
uint256 seedFp = pwalletMain->GetHDChain().seedFp;
if (!seedFp.IsNull())
obj.push_back(Pair("seedfp", seedFp.GetHex()));
return obj;
}

View File

@@ -14,6 +14,7 @@
#include "key_io.h"
#include "main.h"
#include "net.h"
#include "rpc/protocol.h"
#include "script/script.h"
#include "script/sign.h"
#include "timedata.h"
@@ -91,7 +92,7 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
// Generate a new spending key and return its public payment address
libzcash::PaymentAddress CWallet::GenerateNewZKey()
{
AssertLockHeld(cs_wallet); // mapZKeyMetadata
AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata
// TODO: Add Sapling support
auto k = SproutSpendingKey::random();
auto addr = k.address();
@@ -102,10 +103,10 @@ libzcash::PaymentAddress CWallet::GenerateNewZKey()
// Create new metadata
int64_t nCreationTime = GetTime();
mapZKeyMetadata[addr] = CKeyMetadata(nCreationTime);
mapSproutZKeyMetadata[addr] = CKeyMetadata(nCreationTime);
if (!AddZKey(k))
throw std::runtime_error("CWallet::GenerateNewZKey(): AddZKey failed");
if (!AddSproutZKey(k))
throw std::runtime_error("CWallet::GenerateNewZKey(): AddSproutZKey failed");
return addr;
}
@@ -124,18 +125,21 @@ SaplingPaymentAddress CWallet::GenerateNewSaplingZKey()
throw std::runtime_error("CWallet::GenerateNewSaplingZKey(): HD seed not found");
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
uint32_t bip44CoinType = Params().BIP44CoinType();
// We use a fixed keypath scheme of m/32'/coin_type'/account'
// Derive m/32'
auto m_32h = m.Derive(32 | ZIP32_HARDENED_KEY_LIMIT);
// Derive m/32'/coin_type'
auto m_32h_cth = m_32h.Derive(Params().BIP44CoinType() | ZIP32_HARDENED_KEY_LIMIT);
auto m_32h_cth = m_32h.Derive(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT);
// Derive account key at next index, skip keys already known to the wallet
libzcash::SaplingExtendedSpendingKey xsk;
do
{
xsk = m_32h_cth.Derive(hdChain.saplingAccountCounter | ZIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/32'/" + std::to_string(bip44CoinType) + "'/" + std::to_string(hdChain.saplingAccountCounter) + "'";
metadata.seedFp = hdChain.seedFp;
// Increment childkey index
hdChain.saplingAccountCounter++;
} while (HaveSaplingSpendingKey(xsk.expsk.full_viewing_key()));
@@ -177,9 +181,9 @@ bool CWallet::AddSaplingZKey(
// Add spending key to keystore and persist to disk
bool CWallet::AddZKey(const libzcash::SproutSpendingKey &key)
bool CWallet::AddSproutZKey(const libzcash::SproutSpendingKey &key)
{
AssertLockHeld(cs_wallet); // mapZKeyMetadata
AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata
auto addr = key.address();
if (!CCryptoKeyStore::AddSproutSpendingKey(key))
@@ -195,7 +199,7 @@ bool CWallet::AddZKey(const libzcash::SproutSpendingKey &key)
if (!IsCrypted()) {
return CWalletDB(strWalletFile).WriteZKey(addr,
key,
mapZKeyMetadata[addr]);
mapSproutZKeyMetadata[addr]);
}
return true;
}
@@ -286,12 +290,12 @@ bool CWallet::AddCryptedSproutSpendingKey(
return pwalletdbEncryption->WriteCryptedZKey(address,
rk,
vchCryptedSecret,
mapZKeyMetadata[address]);
mapSproutZKeyMetadata[address]);
} else {
return CWalletDB(strWalletFile).WriteCryptedZKey(address,
rk,
vchCryptedSecret,
mapZKeyMetadata[address]);
mapSproutZKeyMetadata[address]);
}
}
return false;
@@ -323,8 +327,8 @@ bool CWallet::LoadKeyMetadata(const CPubKey &pubkey, const CKeyMetadata &meta)
bool CWallet::LoadZKeyMetadata(const SproutPaymentAddress &addr, const CKeyMetadata &meta)
{
AssertLockHeld(cs_wallet); // mapZKeyMetadata
mapZKeyMetadata[addr] = meta;
AssertLockHeld(cs_wallet); // mapSproutZKeyMetadata
mapSproutZKeyMetadata[addr] = meta;
return true;
}
@@ -4948,14 +4952,9 @@ boost::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator
boost::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator()(
const libzcash::SaplingPaymentAddress &zaddr) const
{
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
libzcash::SaplingExtendedSpendingKey sk;
if (m_wallet->GetSaplingIncomingViewingKey(zaddr, ivk) &&
m_wallet->GetSaplingFullViewingKey(ivk, fvk) &&
m_wallet->GetSaplingSpendingKey(fvk, sk)) {
return libzcash::SpendingKey(sk);
libzcash::SaplingExtendedSpendingKey extsk;
if (m_wallet->GetSaplingExtendedSpendingKey(zaddr, extsk)) {
return libzcash::SpendingKey(extsk);
} else {
return boost::none;
}
@@ -4967,3 +4966,58 @@ boost::optional<libzcash::SpendingKey> GetSpendingKeyForPaymentAddress::operator
// Defaults to InvalidEncoding
return libzcash::SpendingKey();
}
SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SproutSpendingKey &sk) const {
auto addr = sk.address();
if (log){
LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(addr));
}
if (m_wallet->HaveSproutSpendingKey(addr)) {
return KeyAlreadyExists;
} else if (m_wallet-> AddSproutZKey(sk)) {
m_wallet->mapSproutZKeyMetadata[addr].nCreateTime = nTime;
return KeyAdded;
} else {
return KeyNotAdded;
}
}
SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingExtendedSpendingKey &sk) const {
auto fvk = sk.expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto addr = sk.DefaultAddress();
{
if (log){
LogPrint("zrpc", "Importing zaddr %s...\n", EncodePaymentAddress(addr));
}
// Don't throw error in case a key is already there
if (m_wallet->HaveSaplingSpendingKey(fvk)) {
return KeyAlreadyExists;
} else {
if (!m_wallet-> AddSaplingZKey(sk, addr)) {
return KeyNotAdded;
}
// Sapling addresses can't have been used in transactions prior to activation.
if (params.vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight == Consensus::NetworkUpgrade::ALWAYS_ACTIVE) {
m_wallet->mapSaplingZKeyMetadata[ivk].nCreateTime = nTime;
} else {
// 154051200 seconds from epoch is Friday, 26 October 2018 00:00:00 GMT - definitely before Sapling activates
m_wallet->mapSaplingZKeyMetadata[ivk].nCreateTime = std::max((int64_t) 154051200, nTime);
}
if (hdKeypath) {
m_wallet->mapSaplingZKeyMetadata[ivk].hdKeypath = hdKeypath.get();
}
if (seedFpStr) {
uint256 seedFp;
seedFp.SetHex(seedFpStr.get());
m_wallet->mapSaplingZKeyMetadata[ivk].seedFp = seedFp;
}
return KeyAdded;
}
}
}
SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key");
}

View File

@@ -846,7 +846,7 @@ public:
std::set<int64_t> setKeyPool;
std::map<CKeyID, CKeyMetadata> mapKeyMetadata;
std::map<libzcash::SproutPaymentAddress, CKeyMetadata> mapZKeyMetadata;
std::map<libzcash::SproutPaymentAddress, CKeyMetadata> mapSproutZKeyMetadata;
std::map<libzcash::SaplingIncomingViewingKey, CKeyMetadata> mapSaplingZKeyMetadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
@@ -1032,7 +1032,7 @@ public:
//! Generates a new zaddr
libzcash::PaymentAddress GenerateNewZKey();
//! Adds spending key to the store, and saves it to disk
bool AddZKey(const libzcash::SproutSpendingKey &key);
bool AddSproutZKey(const libzcash::SproutSpendingKey &key);
//! Adds spending key to the store, without saving it to disk (used by LoadWallet)
bool LoadZKey(const libzcash::SproutSpendingKey &key);
//! Load spending key metadata (used by LoadWallet)
@@ -1429,4 +1429,38 @@ public:
}
};
enum SpendingKeyAddResult {
KeyAlreadyExists,
KeyAdded,
KeyNotAdded,
};
class AddSpendingKeyToWallet : public boost::static_visitor<SpendingKeyAddResult>
{
private:
CWallet *m_wallet;
const Consensus::Params &params;
int64_t nTime;
boost::optional<std::string> hdKeypath; // currently sapling only
boost::optional<std::string> seedFpStr; // currently sapling only
bool log;
public:
AddSpendingKeyToWallet(CWallet *wallet, const Consensus::Params &params) :
m_wallet(wallet), params(params), nTime(1), hdKeypath(boost::none), seedFpStr(boost::none), log(false) {}
AddSpendingKeyToWallet(
CWallet *wallet,
const Consensus::Params &params,
int64_t _nTime,
boost::optional<std::string> _hdKeypath,
boost::optional<std::string> _seedFp,
bool _log
) : m_wallet(wallet), params(params), nTime(_nTime), hdKeypath(_hdKeypath), seedFpStr(_seedFp), log(_log) {}
SpendingKeyAddResult operator()(const libzcash::SproutSpendingKey &sk) const;
SpendingKeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const;
SpendingKeyAddResult operator()(const libzcash::InvalidEncoding& no) const;
};
#endif // BITCOIN_WALLET_WALLET_H

View File

@@ -77,9 +77,13 @@ public:
class CKeyMetadata
{
public:
static const int CURRENT_VERSION=1;
static const int VERSION_BASIC=1;
static const int VERSION_WITH_HDDATA=10;
static const int CURRENT_VERSION=VERSION_WITH_HDDATA;
int nVersion;
int64_t nCreateTime; // 0 means unknown
std::string hdKeypath; //optional HD/zip32 keypath
uint256 seedFp;
CKeyMetadata()
{
@@ -87,7 +91,7 @@ public:
}
CKeyMetadata(int64_t nCreateTime_)
{
nVersion = CKeyMetadata::CURRENT_VERSION;
SetNull();
nCreateTime = nCreateTime_;
}
@@ -97,12 +101,19 @@ public:
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(this->nVersion);
READWRITE(nCreateTime);
if (this->nVersion >= VERSION_WITH_HDDATA)
{
READWRITE(hdKeypath);
READWRITE(seedFp);
}
}
void SetNull()
{
nVersion = CKeyMetadata::CURRENT_VERSION;
nCreateTime = 0;
hdKeypath.clear();
seedFp.SetNull();
}
};

View File

@@ -8,6 +8,7 @@
#include "random.h"
#include "streams.h"
#include "version.h"
#include "zcash/prf.h"
#include <librustzcash.h>
#include <sodium.h>
@@ -15,6 +16,9 @@
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'};
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)
{
assert(len >= 32);
@@ -30,6 +34,29 @@ uint256 HDSeed::Fingerprint() const
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 {
boost::optional<SaplingExtendedFullViewingKey> SaplingExtendedFullViewingKey::Derive(uint32_t i) const

View File

@@ -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 {
typedef blob88 diversifier_index_t;