Auto merge of #3888 - Eirik0:3873-sapling-migration, r=Eirik0

Sapling migration RPC

Closes #3873
This commit is contained in:
Homu 2019-04-27 12:29:29 -07:00
commit 20821614a8
10 changed files with 457 additions and 21 deletions

View File

@ -71,6 +71,7 @@ testScripts=(
'p2p_node_bloom.py'
'regtest_signrawtransaction.py'
'finalsaplingroot.py'
'sprout_sapling_migration.py'
'turnstile.py'
);
testScriptsExt=(

View File

@ -0,0 +1,101 @@
#!/usr/bin/env python
# Copyright (c) 2019 The Zcash developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import sys; assert sys.version_info < (3,), ur"This script does not run under Python 3. Please use Python 2.7.x."
from decimal import Decimal
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import assert_equal, assert_true, get_coinbase_address, \
initialize_chain_clean, start_nodes, wait_and_assert_operationid_status, \
wait_and_assert_operationid_status_result
class SproutSaplingMigration(BitcoinTestFramework):
def setup_nodes(self):
return start_nodes(4, self.options.tmpdir, [[
'-nuparams=5ba81b19:100', # Overwinter
'-nuparams=76b809bb:100', # Sapling
]] * 4)
def setup_chain(self):
print("Initializing test directory " + self.options.tmpdir)
initialize_chain_clean(self.options.tmpdir, 4)
def run_test(self):
print "Mining blocks..."
self.nodes[0].generate(101)
self.sync_all()
# Send some ZEC to a Sprout address
tAddr = get_coinbase_address(self.nodes[0])
sproutAddr = self.nodes[0].z_getnewaddress('sprout')
saplingAddr = self.nodes[0].z_getnewaddress('sapling')
opid = self.nodes[0].z_sendmany(tAddr, [{"address": sproutAddr, "amount": Decimal('10')}], 1, 0)
wait_and_assert_operationid_status(self.nodes[0], opid)
self.nodes[0].generate(1)
self.sync_all()
assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('10'))
assert_equal(self.nodes[0].z_getbalance(saplingAddr), Decimal('0'))
# Migrate
self.nodes[0].z_setmigration(True)
print "Mining to block 494..."
self.nodes[0].generate(392) # 102 -> 494
self.sync_all()
# At 494 we should have no async operations
assert_equal(0, len(self.nodes[0].z_getoperationstatus()), "num async operations at 494")
self.nodes[0].generate(1)
self.sync_all()
# At 495 we should have an async operation
operationstatus = self.nodes[0].z_getoperationstatus()
print "migration operation: {}".format(operationstatus)
assert_equal(1, len(operationstatus), "num async operations at 495")
assert_equal('saplingmigration', operationstatus[0]['method'])
assert_equal(500, operationstatus[0]['target_height'])
result = wait_and_assert_operationid_status_result(self.nodes[0], operationstatus[0]['id'])
print "result: {}".format(result)
assert_equal('saplingmigration', result['method'])
assert_equal(500, result['target_height'])
assert_equal(1, result['result']['num_tx_created'])
assert_equal(0, len(self.nodes[0].getrawmempool()), "mempool size at 495")
self.nodes[0].generate(3)
self.sync_all()
# At 498 the mempool will be empty and no funds will have moved
assert_equal(0, len(self.nodes[0].getrawmempool()), "mempool size at 498")
assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('10'))
assert_equal(self.nodes[0].z_getbalance(saplingAddr), Decimal('0'))
self.nodes[0].generate(1)
self.sync_all()
# At 499 there will be a transaction in the mempool and the note will be locked
assert_equal(1, len(self.nodes[0].getrawmempool()), "mempool size at 499")
assert_equal(self.nodes[0].z_getbalance(sproutAddr), Decimal('0'))
assert_equal(self.nodes[0].z_getbalance(saplingAddr), Decimal('0'))
assert_true(self.nodes[0].z_getbalance(saplingAddr, 0) > Decimal('0'), "Unconfirmed sapling")
self.nodes[0].generate(1)
self.sync_all()
# At 500 funds will have moved
sprout_balance = self.nodes[0].z_getbalance(sproutAddr)
sapling_balance = self.nodes[0].z_getbalance(saplingAddr)
print "sprout balance: {}, sapling balance: {}".format(sprout_balance, sapling_balance)
assert_true(sprout_balance < Decimal('10'), "Should have less Sprout funds")
assert_true(sapling_balance > Decimal('0'), "Should have more Sapling funds")
assert_true(sprout_balance + sapling_balance, Decimal('9.9999'))
if __name__ == '__main__':
SproutSaplingMigration().main()

View File

@ -385,8 +385,9 @@ def assert_raises(exc, fun, *args, **kwds):
def fail(message=""):
raise AssertionError(message)
# Returns txid if operation was a success or None
def wait_and_assert_operationid_status(node, myopid, in_status='success', in_errormsg=None, timeout=300):
# Returns an async operation result
def wait_and_assert_operationid_status_result(node, myopid, in_status='success', in_errormsg=None, timeout=300):
print('waiting for async operation {}'.format(myopid))
result = None
for _ in xrange(1, timeout):
@ -399,26 +400,29 @@ def wait_and_assert_operationid_status(node, myopid, in_status='success', in_err
assert_true(result is not None, "timeout occured")
status = result['status']
txid = None
debug = os.getenv("PYTHON_DEBUG", "")
if debug:
print('...returned status: {}'.format(status))
errormsg = None
if status == "failed":
errormsg = result['error']['message']
elif status == "success":
txid = result['result']['txid']
if os.getenv("PYTHON_DEBUG", ""):
print('...returned status: {}'.format(status))
if errormsg is not None:
if debug:
print('...returned error: {}'.format(errormsg))
assert_equal(in_errormsg, errormsg)
assert_equal(in_status, status, "Operation returned mismatched status. Error Message: {}".format(errormsg))
if errormsg is not None:
assert_true(in_errormsg is not None, "No error retured. Expected: {}".format(errormsg))
assert_true(in_errormsg in errormsg, "Error returned: {}. Error expected: {}".format(errormsg, in_errormsg))
return result # if there was an error return the result
return result
# Returns txid if operation was a success or None
def wait_and_assert_operationid_status(node, myopid, in_status='success', in_errormsg=None, timeout=300):
result = wait_and_assert_operationid_status_result(node, myopid, in_status, in_errormsg, timeout)
if result['status'] == "success":
return result['result']['txid']
else:
return txid # otherwise return the txid
return None
# Find a coinbase address on the node, filtering by the number of UTXOs it has.
# If no filter is provided, returns the coinbase address on the node containing

View File

@ -10,7 +10,7 @@ from test_framework.authproxy import JSONRPCException
from test_framework.mininode import COIN
from test_framework.util import assert_equal, initialize_chain_clean, \
start_nodes, connect_nodes_bi, wait_and_assert_operationid_status, \
get_coinbase_address
wait_and_assert_operationid_status_result, get_coinbase_address
import sys
import timeit
@ -84,7 +84,7 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
recipients= [{"address":myzaddr, "amount": Decimal('1')}]
myopid = self.nodes[3].z_sendmany(mytaddr, recipients)
wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "no UTXOs found for taddr from address", 10)
wait_and_assert_operationid_status(self.nodes[3], myopid, "failed", "Insufficient funds, no UTXOs found for taddr from address.", 10)
# This send will fail because our wallet does not allow any change when protecting a coinbase utxo,
# as it's currently not possible to specify a change address in z_sendmany.
@ -92,7 +92,9 @@ class WalletProtectCoinbaseTest (BitcoinTestFramework):
recipients.append({"address":myzaddr, "amount":Decimal('1.23456789')})
myopid = self.nodes[0].z_sendmany(mytaddr, recipients)
error_result = wait_and_assert_operationid_status(self.nodes[0], myopid, "failed", "wallet does not allow any change", 10)
error_result = wait_and_assert_operationid_status_result(self.nodes[0], myopid, "failed", ("Change 8.76533211 not allowed. "
"When shielding coinbase funds, the wallet does not allow any change "
"as there is currently no way to specify a change address in z_sendmany."), 10)
# Test that the returned status object contains a params field with the operation's input parameters
assert_equal(error_result["method"], "z_sendmany")

View File

@ -217,6 +217,7 @@ BITCOIN_CORE_H = \
validationinterface.h \
version.h \
wallet/asyncrpcoperation_mergetoaddress.h \
wallet/asyncrpcoperation_saplingmigration.h \
wallet/asyncrpcoperation_sendmany.h \
wallet/asyncrpcoperation_shieldcoinbase.h \
wallet/crypter.h \
@ -306,6 +307,7 @@ libbitcoin_wallet_a_SOURCES = \
zcbenchmarks.cpp \
zcbenchmarks.h \
wallet/asyncrpcoperation_mergetoaddress.cpp \
wallet/asyncrpcoperation_saplingmigration.cpp \
wallet/asyncrpcoperation_sendmany.cpp \
wallet/asyncrpcoperation_shieldcoinbase.cpp \
wallet/crypter.cpp \

View File

@ -0,0 +1,198 @@
#include "assert.h"
#include "boost/variant/static_visitor.hpp"
#include "asyncrpcoperation_saplingmigration.h"
#include "init.h"
#include "rpc/protocol.h"
#include "random.h"
#include "sync.h"
#include "tinyformat.h"
#include "transaction_builder.h"
#include "util.h"
#include "wallet.h"
const CAmount FEE = 10000;
AsyncRPCOperation_saplingmigration::AsyncRPCOperation_saplingmigration(int targetHeight) : targetHeight_(targetHeight) {}
AsyncRPCOperation_saplingmigration::~AsyncRPCOperation_saplingmigration() {}
void AsyncRPCOperation_saplingmigration::main() {
if (isCancelled())
return;
set_state(OperationStatus::EXECUTING);
start_execution_clock();
bool success = false;
try {
success = main_impl();
} catch (const UniValue& objError) {
int code = find_value(objError, "code").get_int();
std::string message = find_value(objError, "message").get_str();
set_error_code(code);
set_error_message(message);
} catch (const runtime_error& e) {
set_error_code(-1);
set_error_message("runtime error: " + string(e.what()));
} catch (const logic_error& e) {
set_error_code(-1);
set_error_message("logic error: " + string(e.what()));
} catch (const exception& e) {
set_error_code(-1);
set_error_message("general exception: " + string(e.what()));
} catch (...) {
set_error_code(-2);
set_error_message("unknown error");
}
stop_execution_clock();
if (success) {
set_state(OperationStatus::SUCCESS);
} else {
set_state(OperationStatus::FAILED);
}
std::string s = strprintf("%s: Sprout->Sapling transactions created. (status=%s", getId(), getStateAsString());
if (success) {
s += strprintf(", success)\n");
} else {
s += strprintf(", error=%s)\n", getErrorMessage());
}
LogPrintf("%s", s);
}
bool AsyncRPCOperation_saplingmigration::main_impl() {
std::vector<CSproutNotePlaintextEntry> sproutEntries;
std::vector<SaplingNoteEntry> saplingEntries;
{
LOCK2(cs_main, pwalletMain->cs_wallet);
// We set minDepth to 11 to avoid unconfirmed notes and in anticipation of specifying
// an anchor at height N-10 for each Sprout JoinSplit description
// Consider, should notes be sorted?
pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, "", 11);
}
CAmount availableFunds = 0;
for (const CSproutNotePlaintextEntry& sproutEntry : sproutEntries) {
availableFunds = sproutEntry.plaintext.value();
}
// If the remaining amount to be migrated is less than 0.01 ZEC, end the migration.
if (availableFunds < CENT) {
setMigrationResult(0);
return true;
}
HDSeed seed;
if (!pwalletMain->GetHDSeed(seed)) {
throw JSONRPCError(
RPC_WALLET_ERROR,
"AsyncRPCOperation_AsyncRPCOperation_saplingmigration: HD seed not found");
}
libzcash::SaplingPaymentAddress migrationDestAddress = getMigrationDestAddress(seed);
auto consensusParams = Params().GetConsensus();
// Up to the limit of 5, as many transactions are sent as are needed to migrate the remaining funds
int numTxCreated = 0;
int noteIndex = 0;
do {
CAmount amountToSend = chooseAmount(availableFunds);
auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain, pzcashParams);
std::vector<CSproutNotePlaintextEntry> fromNotes;
CAmount fromNoteAmount = 0;
while (fromNoteAmount < amountToSend) {
auto sproutEntry = sproutEntries[noteIndex++];
fromNotes.push_back(sproutEntry);
fromNoteAmount += sproutEntry.plaintext.value();
}
availableFunds -= fromNoteAmount;
for (const CSproutNotePlaintextEntry& sproutEntry : fromNotes) {
libzcash::SproutNote sproutNote = sproutEntry.plaintext.note(sproutEntry.address);
libzcash::SproutSpendingKey sproutSk;
pwalletMain->GetSproutSpendingKey(sproutEntry.address, sproutSk);
std::vector<JSOutPoint> vOutPoints = {sproutEntry.jsop};
// Each migration transaction SHOULD specify an anchor at height N-10
// for each Sprout JoinSplit description
// TODO: the above functionality (in comment) is not implemented in zcashd
uint256 inputAnchor;
std::vector<boost::optional<SproutWitness>> vInputWitnesses;
pwalletMain->GetSproutNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor);
builder.AddSproutInput(sproutSk, sproutNote, vInputWitnesses[0].get());
}
// The amount chosen *includes* the 0.0001 ZEC fee for this transaction, i.e.
// the value of the Sapling output will be 0.0001 ZEC less.
builder.SetFee(FEE);
builder.AddSaplingOutput(ovkForShieldingFromTaddr(seed), migrationDestAddress, amountToSend - FEE);
CTransaction tx = builder.Build().GetTxOrThrow();
if (isCancelled()) {
break;
}
pwalletMain->AddPendingSaplingMigrationTx(tx);
++numTxCreated;
} while (numTxCreated < 5 && availableFunds > CENT);
setMigrationResult(numTxCreated);
return true;
}
void AsyncRPCOperation_saplingmigration::setMigrationResult(int numTxCreated) {
UniValue res(UniValue::VOBJ);
res.push_back(Pair("num_tx_created", numTxCreated));
set_result(res);
}
CAmount AsyncRPCOperation_saplingmigration::chooseAmount(const CAmount& availableFunds) {
CAmount amount = 0;
do {
// 1. Choose an integer exponent uniformly in the range 6 to 8 inclusive.
int exponent = GetRand(3) + 6;
// 2. Choose an integer mantissa uniformly in the range 1 to 99 inclusive.
uint64_t mantissa = GetRand(99) + 1;
// 3. Calculate amount := (mantissa * 10^exponent) zatoshi.
int pow = std::pow(10, exponent);
amount = mantissa * pow;
// 4. If amount is greater than the amount remaining to send, repeat from step 1.
} while (amount > availableFunds);
return amount;
}
// Unless otherwise specified, the migration destination address is the address for Sapling account 0
libzcash::SaplingPaymentAddress AsyncRPCOperation_saplingmigration::getMigrationDestAddress(const HDSeed& seed) {
// Derive the address for Sapling account 0
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(bip44CoinType | ZIP32_HARDENED_KEY_LIMIT);
// Derive m/32'/coin_type'/0'
libzcash::SaplingExtendedSpendingKey xsk = m_32h_cth.Derive(0 | ZIP32_HARDENED_KEY_LIMIT);
libzcash::SaplingPaymentAddress toAddress = xsk.DefaultAddress();
// Refactor: this is similar logic as in the visitor HaveSpendingKeyForPaymentAddress and is used elsewhere
libzcash::SaplingIncomingViewingKey ivk;
libzcash::SaplingFullViewingKey fvk;
if (!(pwalletMain->GetSaplingIncomingViewingKey(toAddress, ivk) &&
pwalletMain->GetSaplingFullViewingKey(ivk, fvk) &&
pwalletMain->HaveSaplingSpendingKey(fvk))) {
// Sapling account 0 must be the first address returned by GenerateNewSaplingZKey
assert(pwalletMain->GenerateNewSaplingZKey() == toAddress);
}
return toAddress;
}
UniValue AsyncRPCOperation_saplingmigration::getStatus() const {
UniValue v = AsyncRPCOperation::getStatus();
UniValue obj = v.get_obj();
obj.push_back(Pair("method", "saplingmigration"));
obj.push_back(Pair("target_height", targetHeight_));
return obj;
}

View File

@ -0,0 +1,33 @@
#include "amount.h"
#include "asyncrpcoperation.h"
#include "univalue.h"
#include "zcash/Address.hpp"
#include "zcash/zip32.h"
class AsyncRPCOperation_saplingmigration : public AsyncRPCOperation
{
public:
AsyncRPCOperation_saplingmigration(int targetHeight);
virtual ~AsyncRPCOperation_saplingmigration();
// We don't want to be copied or moved around
AsyncRPCOperation_saplingmigration(AsyncRPCOperation_saplingmigration const&) = delete; // Copy construct
AsyncRPCOperation_saplingmigration(AsyncRPCOperation_saplingmigration&&) = delete; // Move construct
AsyncRPCOperation_saplingmigration& operator=(AsyncRPCOperation_saplingmigration const&) = delete; // Copy assign
AsyncRPCOperation_saplingmigration& operator=(AsyncRPCOperation_saplingmigration&&) = delete; // Move assign
virtual void main();
virtual UniValue getStatus() const;
private:
int targetHeight_;
bool main_impl();
void setMigrationResult(int numTxCreated);
CAmount chooseAmount(const CAmount& availableFunds);
libzcash::SaplingPaymentAddress getMigrationDestAddress(const HDSeed& seed);
};

View File

@ -3906,6 +3906,28 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
return operationId;
}
UniValue z_setmigration(const UniValue& params, bool fHelp) {
if (!EnsureWalletIsAvailable(fHelp))
return NullUniValue;
if (fHelp || params.size() != 1)
throw runtime_error(
"z_setmigration enabled\n"
"When enabled the Sprout to Sapling migration will attempt to migrate all funds from this wallets\n"
"Sprout addresses to either the address for Sapling account 0 or the address specified by the parameter\n"
"'-migrationdestaddress'.\n"
"\n"
"This migration is designed to minimize information leakage. As a result for wallets with a significant\n"
"Sprout balance, this process may take several weeks. The migration works by sending, up to 5, as many\n"
"transactions as possible whenever the blockchain reaches a height equal to 499 modulo 500. The transaction\n"
"amounts are picked according to the random distribution specified in ZIP 308. The migration will end once\n"
"the wallets Sprout balance is below" + strprintf("%s %s", FormatMoney(CENT), CURRENCY_UNIT) + ".\n"
"\nArguments:\n"
"1. enabled (boolean, required) 'true' or 'false' to enable or disable respectively.\n"
);
LOCK(pwalletMain->cs_wallet);
pwalletMain->fSaplingMigrationEnabled = params[0].get_bool();
return NullUniValue;
}
/**
When estimating the number of coinbase utxos we can shield in a single transaction:
@ -4660,6 +4682,7 @@ static const CRPCCommand commands[] =
{ "wallet", "z_gettotalbalance", &z_gettotalbalance, false },
{ "wallet", "z_mergetoaddress", &z_mergetoaddress, false },
{ "wallet", "z_sendmany", &z_sendmany, false },
{ "wallet", "z_setmigration", &z_setmigration, false },
{ "wallet", "z_shieldcoinbase", &z_shieldcoinbase, false },
{ "wallet", "z_getoperationstatus", &z_getoperationstatus, true },
{ "wallet", "z_getoperationresult", &z_getoperationresult, true },

View File

@ -5,8 +5,10 @@
#include "wallet/wallet.h"
#include "asyncrpcqueue.h"
#include "checkpoints.h"
#include "coincontrol.h"
#include "core_io.h"
#include "consensus/upgrades.h"
#include "consensus/validation.h"
#include "consensus/consensus.h"
@ -15,12 +17,14 @@
#include "main.h"
#include "net.h"
#include "rpc/protocol.h"
#include "rpc/server.h"
#include "script/script.h"
#include "script/sign.h"
#include "timedata.h"
#include "utilmoneystr.h"
#include "zcash/Note.hpp"
#include "crypter.h"
#include "wallet/asyncrpcoperation_saplingmigration.h"
#include "zcash/zip32.h"
#include <assert.h>
@ -32,6 +36,8 @@
using namespace std;
using namespace libzcash;
extern UniValue sendrawtransaction(const UniValue& params, bool fHelp);
/**
* Settings
*/
@ -558,6 +564,15 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false;
}
void CWallet::ChainTipAdded(const CBlockIndex *pindex,
const CBlock *pblock,
SproutMerkleTree sproutTree,
SaplingMerkleTree saplingTree)
{
IncrementNoteWitnesses(pindex, pblock, sproutTree, saplingTree);
UpdateSaplingNullifierNoteMapForBlock(pblock);
}
void CWallet::ChainTip(const CBlockIndex *pindex,
const CBlock *pblock,
SproutMerkleTree sproutTree,
@ -565,11 +580,60 @@ void CWallet::ChainTip(const CBlockIndex *pindex,
bool added)
{
if (added) {
IncrementNoteWitnesses(pindex, pblock, sproutTree, saplingTree);
ChainTipAdded(pindex, pblock, sproutTree, saplingTree);
RunSaplingMigration(pindex->nHeight);
} else {
DecrementNoteWitnesses(pindex);
UpdateSaplingNullifierNoteMapForBlock(pblock);
}
UpdateSaplingNullifierNoteMapForBlock(pblock);
}
void CWallet::RunSaplingMigration(int blockHeight) {
if (!NetworkUpgradeActive(blockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) {
return;
}
LOCK(cs_wallet);
if (!fSaplingMigrationEnabled) {
return;
}
// The migration transactions to be sent in a particular batch can take
// significant time to generate, and this time depends on the speed of the user's
// computer. If they were generated only after a block is seen at the target
// height minus 1, then this could leak information. Therefore, for target
// height N, implementations SHOULD start generating the transactions at around
// height N-5
if (blockHeight % 500 == 495) {
if (saplingMigrationOperation != nullptr) {
saplingMigrationOperation->cancel();
}
pendingSaplingMigrationTxs.clear();
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue();
std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_saplingmigration(blockHeight + 5));
saplingMigrationOperation = operation;
q->addOperation(operation);
} else if (blockHeight % 500 == 499) {
if (saplingMigrationOperation != nullptr) {
saplingMigrationOperation->cancel();
}
for (const CTransaction& transaction : pendingSaplingMigrationTxs) {
// The following is taken from z_sendmany/z_mergetoaddress
// Send the transaction
// TODO: Use CWallet::CommitTransaction instead of sendrawtransaction
auto signedtxn = EncodeHexTx(transaction);
UniValue params = UniValue(UniValue::VARR);
params.push_back(signedtxn);
UniValue sendResultValue = sendrawtransaction(params, false);
if (sendResultValue.isNull()) {
throw JSONRPCError(RPC_WALLET_ERROR, "sendrawtransaction did not return an error or a txid.");
}
}
pendingSaplingMigrationTxs.clear();
}
}
void CWallet::AddPendingSaplingMigrationTx(const CTransaction& tx) {
LOCK(cs_wallet);
pendingSaplingMigrationTxs.push_back(tx);
}
void CWallet::SetBestChain(const CBlockLocator& loc)
@ -2450,7 +2514,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
}
}
// Increment note witness caches
ChainTip(pindex, &block, sproutTree, saplingTree, true);
ChainTipAdded(pindex, &block, sproutTree, saplingTree);
pindex = chainActive.Next(pindex);
if (GetTime() >= nNow + 60) {

View File

@ -7,6 +7,7 @@
#define BITCOIN_WALLET_WALLET_H
#include "amount.h"
#include "asyncrpcoperation.h"
#include "coins.h"
#include "key.h"
#include "keystore.h"
@ -755,6 +756,9 @@ private:
TxNullifiers mapTxSproutNullifiers;
TxNullifiers mapTxSaplingNullifiers;
std::vector<CTransaction> pendingSaplingMigrationTxs;
std::shared_ptr<AsyncRPCOperation> saplingMigrationOperation = nullptr;
void AddToTransparentSpends(const COutPoint& outpoint, const uint256& wtxid);
void AddToSproutSpends(const uint256& nullifier, const uint256& wtxid);
void AddToSaplingSpends(const uint256& nullifier, const uint256& wtxid);
@ -767,6 +771,7 @@ public:
* incremental witness cache in any transaction in mapWallet.
*/
int64_t nWitnessCacheSize;
bool fSaplingMigrationEnabled = false;
void ClearNoteWitnessCache();
@ -832,6 +837,7 @@ protected:
private:
template <class T>
void SyncMetaData(std::pair<typename TxSpendMap<T>::iterator, typename TxSpendMap<T>::iterator>);
void ChainTipAdded(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree);
protected:
bool UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx);
@ -1183,6 +1189,8 @@ public:
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetChange(const CTransaction& tx) const;
void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree, bool added);
void RunSaplingMigration(int blockHeight);
void AddPendingSaplingMigrationTx(const CTransaction& tx);
/** Saves witness caches and best block locator to disk. */
void SetBestChain(const CBlockLocator& loc);
std::set<std::pair<libzcash::PaymentAddress, uint256>> GetNullifiersForAddresses(const std::set<libzcash::PaymentAddress> & addresses);