desprout
This commit is contained in:
@@ -430,6 +430,8 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SCENARIO #1
|
* SCENARIO #1
|
||||||
*
|
*
|
||||||
@@ -438,7 +440,6 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
|
|||||||
* There are no zaddrs or joinsplits involved.
|
* There are no zaddrs or joinsplits involved.
|
||||||
*/
|
*/
|
||||||
if (isPureTaddrOnlyTx) {
|
if (isPureTaddrOnlyTx) {
|
||||||
UniValue obj(UniValue::VOBJ);
|
|
||||||
obj.push_back(Pair("rawtxn", EncodeHexTx(tx_)));
|
obj.push_back(Pair("rawtxn", EncodeHexTx(tx_)));
|
||||||
sign_send_raw_transaction(obj);
|
sign_send_raw_transaction(obj);
|
||||||
return true;
|
return true;
|
||||||
@@ -447,335 +448,6 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
|
|||||||
* END SCENARIO #1
|
* END SCENARIO #1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
// Prepare raw transaction to handle JoinSplits
|
|
||||||
CMutableTransaction mtx(tx_);
|
|
||||||
crypto_sign_keypair(joinSplitPubKey_.begin(), joinSplitPrivKey_);
|
|
||||||
mtx.joinSplitPubKey = joinSplitPubKey_;
|
|
||||||
tx_ = CTransaction(mtx);
|
|
||||||
std::string hexMemo = std::get<1>(recipient_);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SCENARIO #2
|
|
||||||
*
|
|
||||||
* taddrs -> zaddr
|
|
||||||
*
|
|
||||||
* We only need a single JoinSplit.
|
|
||||||
*/
|
|
||||||
if (sproutNoteInputs_.empty() && isToZaddr_) {
|
|
||||||
// Create JoinSplit to target z-addr.
|
|
||||||
MergeToAddressJSInfo info;
|
|
||||||
info.vpub_old = sendAmount;
|
|
||||||
info.vpub_new = 0;
|
|
||||||
|
|
||||||
JSOutput jso = JSOutput(boost::get<libzcash::SproutPaymentAddress>(toPaymentAddress_), sendAmount);
|
|
||||||
if (hexMemo.size() > 0) {
|
|
||||||
jso.memo = get_memo_from_hex_string(hexMemo);
|
|
||||||
}
|
|
||||||
info.vjsout.push_back(jso);
|
|
||||||
|
|
||||||
UniValue obj(UniValue::VOBJ);
|
|
||||||
obj = perform_joinsplit(info);
|
|
||||||
sign_send_raw_transaction(obj);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* END SCENARIO #2
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Copy zinputs to more flexible containers
|
|
||||||
std::deque<MergeToAddressInputSproutNote> zInputsDeque;
|
|
||||||
for (const auto& o : sproutNoteInputs_) {
|
|
||||||
zInputsDeque.push_back(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When spending notes, take a snapshot of note witnesses and anchors as the treestate will
|
|
||||||
// change upon arrival of new blocks which contain joinsplit transactions. This is likely
|
|
||||||
// to happen as creating a chained joinsplit transaction can take longer than the block interval.
|
|
||||||
{
|
|
||||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
|
||||||
for (auto t : sproutNoteInputs_) {
|
|
||||||
JSOutPoint jso = std::get<0>(t);
|
|
||||||
std::vector<JSOutPoint> vOutPoints = {jso};
|
|
||||||
uint256 inputAnchor;
|
|
||||||
std::vector<boost::optional<SproutWitness>> vInputWitnesses;
|
|
||||||
pwalletMain->GetSproutNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor);
|
|
||||||
jsopWitnessAnchorMap[jso.ToString()] = MergeToAddressWitnessAnchorData{vInputWitnesses[0], inputAnchor};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SCENARIO #3
|
|
||||||
*
|
|
||||||
* zaddrs -> zaddr
|
|
||||||
* taddrs ->
|
|
||||||
*
|
|
||||||
* zaddrs ->
|
|
||||||
* taddrs -> taddr
|
|
||||||
*
|
|
||||||
* Send to zaddr by chaining JoinSplits together and immediately consuming any change
|
|
||||||
* Send to taddr by creating dummy z outputs and accumulating value in a change note
|
|
||||||
* which is used to set vpub_new in the last chained joinsplit.
|
|
||||||
*/
|
|
||||||
UniValue obj(UniValue::VOBJ);
|
|
||||||
CAmount jsChange = 0; // this is updated after each joinsplit
|
|
||||||
int changeOutputIndex = -1; // this is updated after each joinsplit if jsChange > 0
|
|
||||||
bool vpubOldProcessed = false; // updated when vpub_old for taddr inputs is set in first joinsplit
|
|
||||||
bool vpubNewProcessed = false; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit
|
|
||||||
|
|
||||||
// At this point, we are guaranteed to have at least one input note.
|
|
||||||
// Use address of first input note as the temporary change address.
|
|
||||||
SproutSpendingKey changeKey = std::get<3>(zInputsDeque.front());
|
|
||||||
SproutPaymentAddress changeAddress = changeKey.address();
|
|
||||||
|
|
||||||
CAmount vpubOldTarget = 0;
|
|
||||||
CAmount vpubNewTarget = 0;
|
|
||||||
if (isToTaddr_) {
|
|
||||||
vpubNewTarget = z_inputs_total;
|
|
||||||
} else {
|
|
||||||
if (utxoInputs_.empty()) {
|
|
||||||
vpubNewTarget = minersFee;
|
|
||||||
} else {
|
|
||||||
vpubOldTarget = t_inputs_total - minersFee;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep track of treestate within this transaction
|
|
||||||
boost::unordered_map<uint256, SproutMerkleTree, CCoinsKeyHasher> intermediates;
|
|
||||||
std::vector<uint256> previousCommitments;
|
|
||||||
|
|
||||||
while (!vpubNewProcessed) {
|
|
||||||
MergeToAddressJSInfo info;
|
|
||||||
info.vpub_old = 0;
|
|
||||||
info.vpub_new = 0;
|
|
||||||
|
|
||||||
// Set vpub_old in the first joinsplit
|
|
||||||
if (!vpubOldProcessed) {
|
|
||||||
if (t_inputs_total < vpubOldTarget) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR,
|
|
||||||
strprintf("Insufficient transparent funds for vpub_old %s (miners fee %s, taddr inputs %s)",
|
|
||||||
FormatMoney(vpubOldTarget), FormatMoney(minersFee), FormatMoney(t_inputs_total)));
|
|
||||||
}
|
|
||||||
info.vpub_old += vpubOldTarget; // funds flowing from public pool
|
|
||||||
vpubOldProcessed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
CAmount jsInputValue = 0;
|
|
||||||
uint256 jsAnchor;
|
|
||||||
std::vector<boost::optional<SproutWitness>> witnesses;
|
|
||||||
|
|
||||||
JSDescription prevJoinSplit;
|
|
||||||
|
|
||||||
// Keep track of previous JoinSplit and its commitments
|
|
||||||
if (tx_.vjoinsplit.size() > 0) {
|
|
||||||
prevJoinSplit = tx_.vjoinsplit.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is no change, the chain has terminated so we can reset the tracked treestate.
|
|
||||||
if (jsChange == 0 && tx_.vjoinsplit.size() > 0) {
|
|
||||||
intermediates.clear();
|
|
||||||
previousCommitments.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Consume change as the first input of the JoinSplit.
|
|
||||||
//
|
|
||||||
if (jsChange > 0) {
|
|
||||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
|
||||||
|
|
||||||
// Update tree state with previous joinsplit
|
|
||||||
SproutMerkleTree tree;
|
|
||||||
auto it = intermediates.find(prevJoinSplit.anchor);
|
|
||||||
if (it != intermediates.end()) {
|
|
||||||
tree = it->second;
|
|
||||||
} else if (!pcoinsTip->GetSproutAnchorAt(prevJoinSplit.anchor, tree)) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Could not find previous JoinSplit anchor");
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(changeOutputIndex != -1);
|
|
||||||
boost::optional<SproutWitness> changeWitness;
|
|
||||||
int n = 0;
|
|
||||||
for (const uint256& commitment : prevJoinSplit.commitments) {
|
|
||||||
tree.append(commitment);
|
|
||||||
previousCommitments.push_back(commitment);
|
|
||||||
if (!changeWitness && changeOutputIndex == n++) {
|
|
||||||
changeWitness = tree.witness();
|
|
||||||
} else if (changeWitness) {
|
|
||||||
changeWitness.get().append(commitment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (changeWitness) {
|
|
||||||
witnesses.push_back(changeWitness);
|
|
||||||
}
|
|
||||||
jsAnchor = tree.root();
|
|
||||||
intermediates.insert(std::make_pair(tree.root(), tree)); // chained js are interstitial (found in between block boundaries)
|
|
||||||
|
|
||||||
// Decrypt the change note's ciphertext to retrieve some data we need
|
|
||||||
ZCNoteDecryption decryptor(changeKey.receiving_key());
|
|
||||||
auto hSig = prevJoinSplit.h_sig(*pzcashParams, tx_.joinSplitPubKey);
|
|
||||||
try {
|
|
||||||
SproutNotePlaintext plaintext = SproutNotePlaintext::decrypt(
|
|
||||||
decryptor,
|
|
||||||
prevJoinSplit.ciphertexts[changeOutputIndex],
|
|
||||||
prevJoinSplit.ephemeralKey,
|
|
||||||
hSig,
|
|
||||||
(unsigned char)changeOutputIndex);
|
|
||||||
|
|
||||||
SproutNote note = plaintext.note(changeAddress);
|
|
||||||
info.notes.push_back(note);
|
|
||||||
info.zkeys.push_back(changeKey);
|
|
||||||
|
|
||||||
jsInputValue += plaintext.value();
|
|
||||||
|
|
||||||
LogPrint("zrpcunsafe", "%s: spending change (amount=%s)\n",
|
|
||||||
getId(),
|
|
||||||
FormatMoney(plaintext.value()));
|
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Consume spendable non-change notes
|
|
||||||
//
|
|
||||||
std::vector<SproutNote> vInputNotes;
|
|
||||||
std::vector<SproutSpendingKey> vInputZKeys;
|
|
||||||
std::vector<JSOutPoint> vOutPoints;
|
|
||||||
std::vector<boost::optional<SproutWitness>> vInputWitnesses;
|
|
||||||
uint256 inputAnchor;
|
|
||||||
int numInputsNeeded = (jsChange > 0) ? 1 : 0;
|
|
||||||
while (numInputsNeeded++ < ZC_NUM_JS_INPUTS && zInputsDeque.size() > 0) {
|
|
||||||
MergeToAddressInputSproutNote t = zInputsDeque.front();
|
|
||||||
JSOutPoint jso = std::get<0>(t);
|
|
||||||
SproutNote note = std::get<1>(t);
|
|
||||||
CAmount noteFunds = std::get<2>(t);
|
|
||||||
SproutSpendingKey zkey = std::get<3>(t);
|
|
||||||
zInputsDeque.pop_front();
|
|
||||||
|
|
||||||
MergeToAddressWitnessAnchorData wad = jsopWitnessAnchorMap[jso.ToString()];
|
|
||||||
vInputWitnesses.push_back(wad.witness);
|
|
||||||
if (inputAnchor.IsNull()) {
|
|
||||||
inputAnchor = wad.anchor;
|
|
||||||
} else if (inputAnchor != wad.anchor) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Selected input notes do not share the same anchor");
|
|
||||||
}
|
|
||||||
|
|
||||||
vOutPoints.push_back(jso);
|
|
||||||
vInputNotes.push_back(note);
|
|
||||||
vInputZKeys.push_back(zkey);
|
|
||||||
|
|
||||||
jsInputValue += noteFunds;
|
|
||||||
|
|
||||||
int wtxHeight = -1;
|
|
||||||
int wtxDepth = -1;
|
|
||||||
{
|
|
||||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
|
||||||
const CWalletTx& wtx = pwalletMain->mapWallet[jso.hash];
|
|
||||||
// Zero confirmation notes belong to transactions which have not yet been mined
|
|
||||||
if (mapBlockIndex.find(wtx.hashBlock) == mapBlockIndex.end()) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("mapBlockIndex does not contain block hash %s", wtx.hashBlock.ToString()));
|
|
||||||
}
|
|
||||||
wtxHeight = komodo_blockheight(wtx.hashBlock);
|
|
||||||
wtxDepth = wtx.GetDepthInMainChain();
|
|
||||||
}
|
|
||||||
LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n",
|
|
||||||
getId(),
|
|
||||||
jso.hash.ToString().substr(0, 10),
|
|
||||||
jso.js,
|
|
||||||
int(jso.n), // uint8_t
|
|
||||||
FormatMoney(noteFunds),
|
|
||||||
wtxHeight,
|
|
||||||
wtxDepth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add history of previous commitments to witness
|
|
||||||
if (vInputNotes.size() > 0) {
|
|
||||||
if (vInputWitnesses.size() == 0) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Could not find witness for note commitment");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& optionalWitness : vInputWitnesses) {
|
|
||||||
if (!optionalWitness) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Witness for note commitment is null");
|
|
||||||
}
|
|
||||||
SproutWitness w = *optionalWitness; // could use .get();
|
|
||||||
if (jsChange > 0) {
|
|
||||||
for (const uint256& commitment : previousCommitments) {
|
|
||||||
w.append(commitment);
|
|
||||||
}
|
|
||||||
if (jsAnchor != w.root()) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, "Witness for spendable note does not have same anchor as change input");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
witnesses.push_back(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The jsAnchor is null if this JoinSplit is at the start of a new chain
|
|
||||||
if (jsAnchor.IsNull()) {
|
|
||||||
jsAnchor = inputAnchor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add spendable notes as inputs
|
|
||||||
std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes));
|
|
||||||
std::copy(vInputZKeys.begin(), vInputZKeys.end(), std::back_inserter(info.zkeys));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accumulate change
|
|
||||||
jsChange = jsInputValue + info.vpub_old;
|
|
||||||
|
|
||||||
// Set vpub_new in the last joinsplit (when there are no more notes to spend)
|
|
||||||
if (zInputsDeque.empty()) {
|
|
||||||
assert(!vpubNewProcessed);
|
|
||||||
if (jsInputValue < vpubNewTarget) {
|
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR,
|
|
||||||
strprintf("Insufficient funds for vpub_new %s (miners fee %s, taddr inputs %s)",
|
|
||||||
FormatMoney(vpubNewTarget), FormatMoney(minersFee), FormatMoney(t_inputs_total)));
|
|
||||||
}
|
|
||||||
info.vpub_new += vpubNewTarget; // funds flowing back to public pool
|
|
||||||
vpubNewProcessed = true;
|
|
||||||
jsChange -= vpubNewTarget;
|
|
||||||
// If we are merging to a t-addr, there should be no change
|
|
||||||
if (isToTaddr_) assert(jsChange == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create dummy output
|
|
||||||
info.vjsout.push_back(JSOutput()); // dummy output while we accumulate funds into a change note for vpub_new
|
|
||||||
|
|
||||||
// create output for any change
|
|
||||||
if (jsChange > 0) {
|
|
||||||
std::string outputType = "change";
|
|
||||||
auto jso = JSOutput(changeAddress, jsChange);
|
|
||||||
// If this is the final output, set the target and memo
|
|
||||||
if (isToZaddr_ && vpubNewProcessed) {
|
|
||||||
outputType = "target";
|
|
||||||
jso.addr = boost::get<libzcash::SproutPaymentAddress>(toPaymentAddress_);
|
|
||||||
if (!hexMemo.empty()) {
|
|
||||||
jso.memo = get_memo_from_hex_string(hexMemo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info.vjsout.push_back(jso);
|
|
||||||
|
|
||||||
LogPrint("zrpcunsafe", "%s: generating note for %s (amount=%s)\n",
|
|
||||||
getId(),
|
|
||||||
outputType,
|
|
||||||
FormatMoney(jsChange));
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = perform_joinsplit(info, witnesses, jsAnchor);
|
|
||||||
|
|
||||||
if (jsChange > 0) {
|
|
||||||
changeOutputIndex = mta_find_output(obj, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanity check in case changes to code block above exits loop by invoking 'break'
|
|
||||||
assert(zInputsDeque.size() == 0);
|
|
||||||
assert(vpubNewProcessed);
|
|
||||||
|
|
||||||
sign_send_raw_transaction(obj);
|
sign_send_raw_transaction(obj);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -866,10 +538,6 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(MergeToAddressJSInf
|
|||||||
{
|
{
|
||||||
std::vector<boost::optional<SproutWitness>> witnesses;
|
std::vector<boost::optional<SproutWitness>> witnesses;
|
||||||
uint256 anchor;
|
uint256 anchor;
|
||||||
{
|
|
||||||
LOCK(cs_main);
|
|
||||||
pwalletMain->GetSproutNoteWitnesses(outPoints, witnesses, anchor);
|
|
||||||
}
|
|
||||||
return perform_joinsplit(info, witnesses, anchor);
|
return perform_joinsplit(info, witnesses, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,29 +93,6 @@ double benchmark_sleep()
|
|||||||
return timer_stop(tv_start);
|
return timer_stop(tv_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
double benchmark_create_joinsplit()
|
|
||||||
{
|
|
||||||
uint256 joinSplitPubKey;
|
|
||||||
|
|
||||||
/* Get the anchor of an empty commitment tree. */
|
|
||||||
uint256 anchor = SproutMerkleTree().root();
|
|
||||||
|
|
||||||
struct timeval tv_start;
|
|
||||||
timer_start(tv_start);
|
|
||||||
JSDescription jsdesc(*pzcashParams,
|
|
||||||
joinSplitPubKey,
|
|
||||||
anchor,
|
|
||||||
{JSInput(), JSInput()},
|
|
||||||
{JSOutput(), JSOutput()},
|
|
||||||
0,
|
|
||||||
0);
|
|
||||||
double ret = timer_stop(tv_start);
|
|
||||||
|
|
||||||
auto verifier = libzcash::ProofVerifier::Strict();
|
|
||||||
assert(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<double> benchmark_create_joinsplit_threaded(int nThreads)
|
std::vector<double> benchmark_create_joinsplit_threaded(int nThreads)
|
||||||
{
|
{
|
||||||
std::vector<double> ret;
|
std::vector<double> ret;
|
||||||
@@ -263,160 +240,6 @@ double benchmark_large_tx(size_t nInputs)
|
|||||||
return timer_stop(tv_start);
|
return timer_stop(tv_start);
|
||||||
}
|
}
|
||||||
|
|
||||||
double benchmark_try_decrypt_notes(size_t nAddrs)
|
|
||||||
{
|
|
||||||
CWallet wallet;
|
|
||||||
for (int i = 0; i < nAddrs; i++) {
|
|
||||||
auto sk = libzcash::SproutSpendingKey::random();
|
|
||||||
wallet.AddSproutSpendingKey(sk);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sk = libzcash::SproutSpendingKey::random();
|
|
||||||
auto tx = GetValidReceive(*pzcashParams, sk, 10, true);
|
|
||||||
|
|
||||||
struct timeval tv_start;
|
|
||||||
timer_start(tv_start);
|
|
||||||
auto nd = wallet.FindMySproutNotes(tx);
|
|
||||||
return timer_stop(tv_start);
|
|
||||||
}
|
|
||||||
|
|
||||||
double benchmark_increment_note_witnesses(size_t nTxs)
|
|
||||||
{
|
|
||||||
CWallet wallet;
|
|
||||||
SproutMerkleTree sproutTree;
|
|
||||||
SaplingMerkleTree saplingTree;
|
|
||||||
|
|
||||||
auto sk = libzcash::SproutSpendingKey::random();
|
|
||||||
wallet.AddSproutSpendingKey(sk);
|
|
||||||
|
|
||||||
// First block
|
|
||||||
CBlock block1;
|
|
||||||
for (int i = 0; i < nTxs; i++) {
|
|
||||||
auto wtx = GetValidReceive(*pzcashParams, sk, 10, true);
|
|
||||||
auto note = GetNote(*pzcashParams, sk, wtx, 0, 1);
|
|
||||||
auto nullifier = note.nullifier(sk);
|
|
||||||
|
|
||||||
mapSproutNoteData_t noteData;
|
|
||||||
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
|
|
||||||
SproutNoteData nd {sk.address(), nullifier};
|
|
||||||
noteData[jsoutpt] = nd;
|
|
||||||
|
|
||||||
wtx.SetSproutNoteData(noteData);
|
|
||||||
wallet.AddToWallet(wtx, true, NULL);
|
|
||||||
block1.vtx.push_back(wtx);
|
|
||||||
}
|
|
||||||
CBlockIndex index1(block1);
|
|
||||||
index1.SetHeight(1);
|
|
||||||
|
|
||||||
// Increment to get transactions witnessed
|
|
||||||
wallet.ChainTip(&index1, &block1, std::make_pair(sproutTree, saplingTree));
|
|
||||||
|
|
||||||
// Second block
|
|
||||||
CBlock block2;
|
|
||||||
block2.hashPrevBlock = block1.GetHash();
|
|
||||||
{
|
|
||||||
auto wtx = GetValidReceive(*pzcashParams, sk, 10, true);
|
|
||||||
auto note = GetNote(*pzcashParams, sk, wtx, 0, 1);
|
|
||||||
auto nullifier = note.nullifier(sk);
|
|
||||||
|
|
||||||
mapSproutNoteData_t noteData;
|
|
||||||
JSOutPoint jsoutpt {wtx.GetHash(), 0, 1};
|
|
||||||
SproutNoteData nd {sk.address(), nullifier};
|
|
||||||
noteData[jsoutpt] = nd;
|
|
||||||
|
|
||||||
wtx.SetSproutNoteData(noteData);
|
|
||||||
wallet.AddToWallet(wtx, true, NULL);
|
|
||||||
block2.vtx.push_back(wtx);
|
|
||||||
}
|
|
||||||
CBlockIndex index2(block2);
|
|
||||||
index2.SetHeight(2);
|
|
||||||
|
|
||||||
struct timeval tv_start;
|
|
||||||
timer_start(tv_start);
|
|
||||||
wallet.ChainTip(&index2, &block2, std::make_pair(sproutTree, saplingTree));
|
|
||||||
return timer_stop(tv_start);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fake the input of a given block
|
|
||||||
class FakeCoinsViewDB : public CCoinsViewDB {
|
|
||||||
uint256 hash;
|
|
||||||
SproutMerkleTree t;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FakeCoinsViewDB(std::string dbName, uint256& hash) : CCoinsViewDB(dbName, 100, false, false), hash(hash) {}
|
|
||||||
|
|
||||||
bool GetAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
|
|
||||||
if (rt == t.root()) {
|
|
||||||
tree = t;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 GetBestBlock() const {
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint256 GetBestAnchor() const {
|
|
||||||
return t.root();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BatchWrite(CCoinsMap &mapCoins,
|
|
||||||
const uint256 &hashBlock,
|
|
||||||
const uint256 &hashAnchor,
|
|
||||||
CAnchorsSproutMap &mapSproutAnchors,
|
|
||||||
CNullifiersMap &mapSproutNullifiers,
|
|
||||||
CNullifiersMap& mapSaplingNullifiers) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetStats(CCoinsStats &stats) const {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
double benchmark_connectblock_slow()
|
|
||||||
{
|
|
||||||
// Test for issue 2017-05-01.a
|
|
||||||
SelectParams(CBaseChainParams::MAIN);
|
|
||||||
CBlock block;
|
|
||||||
FILE* fp = fopen((GetDataDir() / "benchmark/block-107134.dat").string().c_str(), "rb");
|
|
||||||
if (!fp) throw new std::runtime_error("Failed to open block data file");
|
|
||||||
CAutoFile blkFile(fp, SER_DISK, CLIENT_VERSION);
|
|
||||||
blkFile >> block;
|
|
||||||
blkFile.fclose();
|
|
||||||
|
|
||||||
// Fake its inputs
|
|
||||||
auto hashPrev = uint256S("00000000159a41f468e22135942a567781c3f3dc7ad62257993eb3c69c3f95ef");
|
|
||||||
FakeCoinsViewDB fakeDB("benchmark/block-107134-inputs", hashPrev);
|
|
||||||
CCoinsViewCache view(&fakeDB);
|
|
||||||
|
|
||||||
// Fake the chain
|
|
||||||
CBlockIndex index(block);
|
|
||||||
index.SetHeight(107134);
|
|
||||||
CBlockIndex indexPrev;
|
|
||||||
indexPrev.phashBlock = &hashPrev;
|
|
||||||
indexPrev.SetHeight(index.GetHeight() - 1);
|
|
||||||
index.pprev = &indexPrev;
|
|
||||||
mapBlockIndex.insert(std::make_pair(hashPrev, &indexPrev));
|
|
||||||
|
|
||||||
CValidationState state;
|
|
||||||
struct timeval tv_start;
|
|
||||||
timer_start(tv_start);
|
|
||||||
assert(ConnectBlock(block, state, &index, view, true));
|
|
||||||
auto duration = timer_stop(tv_start);
|
|
||||||
|
|
||||||
// Undo alterations to global state
|
|
||||||
mapBlockIndex.erase(hashPrev);
|
|
||||||
SelectParamsFromCommandLine();
|
|
||||||
|
|
||||||
return duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern UniValue getnewaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
|
extern UniValue getnewaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); // in rpcwallet.cpp
|
||||||
extern UniValue sendtoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk);
|
extern UniValue sendtoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user