From a8ac403db0bde0c202c87646d3d40efcba788d59 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Thu, 7 Jan 2016 12:09:58 -0700 Subject: [PATCH] Added mapAnchors consensus rules, finished zcrawpour/zcrawreceive. Some specifics on consensus changes: * Transactions must be anchored to a real anchor in the chain. * Anchors are pushed and popped during ConnectBlock/DisconnectBlock as appropriate. * DisconnectTip triggers evictions, under some circumstances, of transactions in the mempool which are anchored to roots that are no longer valid. * Commitments append to the tree at the current best root during ConnectBlock. --- depends/packages/libzerocash.mk | 4 +- qa/rpc-tests/zcpour.py | 31 +++++++++ src/coins.cpp | 15 +++++ src/coins.h | 3 + src/main.cpp | 63 ++++++++++++++++- src/rpcserver.cpp | 1 + src/rpcserver.h | 1 + src/txmempool.cpp | 26 ++++++++ src/txmempool.h | 1 + src/undo.h | 2 + src/wallet/rpcwallet.cpp | 115 +++++++++++++++++++++++++++++--- src/wallet/wallet.cpp | 62 +++++++++++++++++ src/wallet/wallet.h | 1 + 13 files changed, 311 insertions(+), 14 deletions(-) diff --git a/depends/packages/libzerocash.mk b/depends/packages/libzerocash.mk index 7e714a01b..538a1e719 100644 --- a/depends/packages/libzerocash.mk +++ b/depends/packages/libzerocash.mk @@ -2,8 +2,8 @@ package=libzerocash $(package)_download_path=https://github.com/Electric-Coin-Company/$(package)/archive/ $(package)_file_name=$(package)-$($(package)_git_commit).tar.gz $(package)_download_file=$($(package)_git_commit).tar.gz -$(package)_sha256_hash=8000a2cdc276ab4ee3ad3cbd7361162424ab0c4794f17d425f25bfca46853af8 -$(package)_git_commit=1503312b1b340495c9f6a3254587c7fe2c3c87d7 +$(package)_sha256_hash=1364a739751bcdda86cfd66d3d019844d116c374d7a7634bfb3e1a47c085f3c0 +$(package)_git_commit=dd5db5815be70f0e4895784cc905df6f1c73cb17 $(package)_dependencies=libsnark crypto++ openssl boost libgmp $(package)_patches= diff --git a/qa/rpc-tests/zcpour.py b/qa/rpc-tests/zcpour.py index 39b4cf97a..95ecf2dce 100755 --- a/qa/rpc-tests/zcpour.py +++ b/qa/rpc-tests/zcpour.py @@ -16,6 +16,35 @@ class PourTxTest(BitcoinTestFramework): # Start with split network: return super(PourTxTest, self).setup_network(True) + def send_pours_around(self): + zckeypair = self.nodes[0].zcrawkeygen() + zcsecretkey = zckeypair["zcsecretkey"] + zcaddress = zckeypair["zcaddress"] + + (total_in, inputs) = gather_inputs(self.nodes[1], 50) + protect_tx = self.nodes[1].createrawtransaction(inputs, {}) + pour_result = self.nodes[1].zcrawpour(protect_tx, {}, {zcaddress:49.9}, 50, 0.1) + + receive_result = self.nodes[1].zcrawreceive(zcsecretkey, pour_result["encryptedbucket1"]) + assert_equal(receive_result["exists"], False) + + protect_tx = self.nodes[1].signrawtransaction(pour_result["rawtxn"]) + self.nodes[1].sendrawtransaction(protect_tx["hex"]) + self.nodes[1].generate(1) + + receive_result = self.nodes[1].zcrawreceive(zcsecretkey, pour_result["encryptedbucket1"]) + assert_equal(receive_result["exists"], True) + + pour_tx = self.nodes[1].createrawtransaction([], {}) + pour_result = self.nodes[1].zcrawpour(pour_tx, {receive_result["bucket"] : zcsecretkey}, {zcaddress: 49.8}, 0, 0.1) + + self.nodes[1].sendrawtransaction(pour_result["rawtxn"]) + self.nodes[1].generate(1) + sync_blocks(self.nodes) + + receive_result = self.nodes[0].zcrawreceive(zcsecretkey, pour_result["encryptedbucket1"]) + assert_equal(receive_result["exists"], True) + def run_test(self): # All nodes should start with 1,250 BTC: starting_balance = 1250 @@ -66,6 +95,8 @@ class PourTxTest(BitcoinTestFramework): print signed_tx_pour self.nodes[0].sendrawtransaction(signed_tx_pour["hex"]) + + self.send_pours_around() if __name__ == '__main__': PourTxTest().main() diff --git a/src/coins.cpp b/src/coins.cpp index 9f79a6e2b..eba383e22 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -387,6 +387,21 @@ CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const return nResult; } +bool CCoinsViewCache::HavePourRequirements(const CTransaction& tx) const +{ + BOOST_FOREACH(const CPourTx &pour, tx.vpour) + { + libzerocash::IncrementalMerkleTree tree(INCREMENTAL_MERKLE_TREE_DEPTH); + if (!GetAnchorAt(pour.anchor, tree)) { + // If we do not have the anchor for the pour, + // it is invalid. + return false; + } + } + + return true; +} + bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const { if (!tx.IsCoinBase()) { diff --git a/src/coins.h b/src/coins.h index ad942a2d6..7f16aa0ff 100644 --- a/src/coins.h +++ b/src/coins.h @@ -517,6 +517,9 @@ public: //! Check whether all prevouts of the transaction are present in the UTXO set represented by this view bool HaveInputs(const CTransaction& tx) const; + //! Check whether all pour requirements (anchors/serials) are satisfied + bool HavePourRequirements(const CTransaction& tx) const; + //! Return priority of tx at height nHeight double GetPriority(const CTransaction &tx, int nHeight) const; diff --git a/src/main.cpp b/src/main.cpp index 544a34fbb..b3b1149ac 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1057,6 +1057,11 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return state.Invalid(error("AcceptToMemoryPool: inputs already spent"), REJECT_DUPLICATE, "bad-txns-inputs-spent"); + // are the pour's requirements met? + if (!view.HavePourRequirements(tx)) + return state.Invalid(error("AcceptToMemoryPool: pour requirements not met"), + REJECT_DUPLICATE, "bad-txns-pour-requirements-not-met"); + // Bring the best block into scope view.GetBestBlock(); @@ -1505,6 +1510,10 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi if (!inputs.HaveInputs(tx)) return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); + // are the pour's requirements met? + if (!inputs.HavePourRequirements(tx)) + return state.Invalid(error("CheckInputs(): %s pour requirements not met", tx.GetHash().ToString())); + // While checking, GetBestBlock() refers to the parent block. // This is also true for mempool checks. CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; @@ -1766,6 +1775,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } + // set the old best anchor back + view.PopAnchor(blockUndo.old_tree_root); + // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); @@ -1953,6 +1965,25 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin std::vector > vPos; vPos.reserve(block.vtx.size()); blockundo.vtxundo.reserve(block.vtx.size() - 1); + + // Construct the incremental merkle tree at the current + // block position, + auto old_tree_root = view.GetBestAnchor(); + libzerocash::IncrementalMerkleTree tree(INCREMENTAL_MERKLE_TREE_DEPTH); + // This should never fail: we should always be able to get the root + // that is on the tip of our chain + assert(view.GetAnchorAt(old_tree_root, tree)); + + { + // Consistency check: the root of the tree we're given should + // match what we asked for. + std::vector newrt_v(32); + tree.getRootValue(newrt_v); + uint256 anchor_received = uint256(newrt_v); + + assert(anchor_received == old_tree_root); + } + for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction &tx = block.vtx[i]; @@ -1969,6 +2000,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), REJECT_INVALID, "bad-txns-inputs-missingorspent"); + // are the pour's requirements met? + if (!view.HavePourRequirements(tx)) + return state.DoS(100, error("ConnectBlock(): pour requirements not met"), + REJECT_INVALID, "bad-txns-pour-requirements-not-met"); + if (fStrictPayToScriptHash) { // Add in sigops done by pay-to-script-hash inputs; @@ -1994,9 +2030,26 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } UpdateCoins(tx, state, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight); + BOOST_FOREACH(const CPourTx &pour, tx.vpour) { + BOOST_FOREACH(const uint256 &bucket_commitment, pour.commitments) { + // Insert the bucket commitments into our temporary tree. + + std::vector index; + std::vector commitment_value(bucket_commitment.begin(), bucket_commitment.end()); + std::vector commitment_bv(ZC_CM_SIZE * 8); + libzerocash::convertBytesVectorToVector(commitment_value, commitment_bv); + tree.insertElement(commitment_bv, index); + } + } + vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } + + tree.prune(); // prune it, so we don't cache intermediate states we don't need + view.PushAnchor(tree); + blockundo.old_tree_root = old_tree_root; + int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); @@ -2225,6 +2278,7 @@ bool static DisconnectTip(CValidationState &state) { if (!ReadBlockFromDisk(block, pindexDelete)) return AbortNode(state, "Failed to read block"); // Apply the block atomically to the chain state. + uint256 anchorBeforeDisconnect = pcoinsTip->GetBestAnchor(); int64_t nStart = GetTimeMicros(); { CCoinsViewCache view(pcoinsTip); @@ -2233,6 +2287,7 @@ bool static DisconnectTip(CValidationState &state) { assert(view.Flush()); } LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + uint256 anchorAfterDisconnect = pcoinsTip->GetBestAnchor(); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(state, FLUSH_STATE_IF_NEEDED)) return false; @@ -2244,6 +2299,11 @@ bool static DisconnectTip(CValidationState &state) { if (tx.IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) mempool.remove(tx, removed, true); } + if (anchorBeforeDisconnect != anchorAfterDisconnect) { + // The anchor may not change between block disconnects, + // in which case we don't want to evict from the mempool yet! + mempool.removeWithAnchor(anchorBeforeDisconnect); + } mempool.removeCoinbaseSpends(pcoinsTip, pindexDelete->nHeight); mempool.check(pcoinsTip); // Update chainActive and related variables. @@ -4536,7 +4596,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, BOOST_FOREACH(uint256 hash, vEraseQueue) EraseOrphanTx(hash); } - else if (fMissingInputs) + // TODO: currently, prohibit pours from entering mapOrphans + else if (fMissingInputs && tx.vpour.size() == 0) { AddOrphanTx(tx, pfrom->GetId()); diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index c81839da8..313c95c29 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -378,6 +378,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "walletpassphrase", &walletpassphrase, true }, { "wallet", "zcrawkeygen", &zc_raw_keygen, true }, { "wallet", "zcrawpour", &zc_raw_pour, true }, + { "wallet", "zcrawreceive", &zc_raw_receive, true } #endif // ENABLE_WALLET }; diff --git a/src/rpcserver.h b/src/rpcserver.h index 890d12f0a..76b66deab 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -210,6 +210,7 @@ extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHe extern json_spirit::Value resendwallettransactions(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value zc_raw_keygen(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value zc_raw_pour(const json_spirit::Array& params, bool fHelp); +extern json_spirit::Value zc_raw_receive(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index c3d1b60cb..b9bd377ef 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -178,6 +178,32 @@ void CTxMemPool::removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned in } } + +void CTxMemPool::removeWithAnchor(const uint256 &invalidRoot) +{ + // If a block is disconnected from the tip, and the root changed, + // we must invalidate transactions from the mempool which spend + // from that root -- almost as though they were spending coinbases + // which are no longer valid to spend due to coinbase maturity. + LOCK(cs); + list transactionsToRemove; + + for (std::map::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + const CTransaction& tx = it->second.GetTx(); + BOOST_FOREACH(const CPourTx& pour, tx.vpour) { + if (pour.anchor == invalidRoot) { + transactionsToRemove.push_back(tx); + break; + } + } + } + + BOOST_FOREACH(const CTransaction& tx, transactionsToRemove) { + list removed; + remove(tx, removed, true); + } +} + void CTxMemPool::removeConflicts(const CTransaction &tx, std::list& removed) { // Remove transactions which depend on inputs of tx, recursively diff --git a/src/txmempool.h b/src/txmempool.h index 7271a5f60..8dcc70c79 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -114,6 +114,7 @@ public: bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true); void remove(const CTransaction &tx, std::list& removed, bool fRecursive = false); + void removeWithAnchor(const uint256 &invalidRoot); void removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight); void removeConflicts(const CTransaction &tx, std::list& removed); void removeForBlock(const std::vector& vtx, unsigned int nBlockHeight, diff --git a/src/undo.h b/src/undo.h index 1c4ed95bf..2c7b74183 100644 --- a/src/undo.h +++ b/src/undo.h @@ -73,12 +73,14 @@ class CBlockUndo { public: std::vector vtxundo; // for all but the coinbase + uint256 old_tree_root; ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(vtxundo); + READWRITE(old_tree_root); } }; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b777e254a..b08965b48 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2344,13 +2344,68 @@ Value listunspent(const Array& params, bool fHelp) return results; } +Value zc_raw_receive(const json_spirit::Array& params, bool fHelp) +{ + /* + zcrawreceive + */ + RPCTypeCheck(params, boost::assign::list_of(str_type)(str_type)); + LOCK(cs_main); + + std::vector a_sk; + std::string sk_enc; + + { + CDataStream ssData(ParseHexV(params[0], "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION); + try { + ssData >> a_sk; + ssData >> sk_enc; + } catch(const std::exception &) { + throw runtime_error( + "zcsecretkey could not be decoded" + ); + } + } + + libzerocash::PrivateAddress zcsecretkey(a_sk, sk_enc); + libzerocash::Address zcaddress(zcsecretkey); + + auto encrypted_bucket_vec = ParseHexV(params[1], "encrypted_bucket"); + std::string encrypted_bucket(encrypted_bucket_vec.begin(), encrypted_bucket_vec.end()); + libzerocash::Coin decrypted_bucket(encrypted_bucket, zcaddress); + + std::vector commitment_v = decrypted_bucket.getCoinCommitment().getCommitmentValue(); + uint256 commitment = uint256(commitment_v); + + assert(pwalletMain != NULL); + libsnark::merkle_authentication_path path(INCREMENTAL_MERKLE_TREE_DEPTH); // We don't care during receive... yet! :) + size_t path_index = 0; + uint256 anchor; + auto found_in_chain = pwalletMain->WitnessBucketCommitment(commitment, path, path_index, anchor); + + CAmount value_of_bucket = decrypted_bucket.getValue(); + + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + { + ss << decrypted_bucket.getValue(); + ss << decrypted_bucket.getRho(); + ss << decrypted_bucket.getR(); + } + + Object result; + result.push_back(Pair("amount", ValueFromAmount(value_of_bucket))); + result.push_back(Pair("bucket", HexStr(ss.begin(), ss.end()))); + result.push_back(Pair("exists", found_in_chain)); + return result; +} + Value zc_raw_pour(const json_spirit::Array& params, bool fHelp) { /* zcrawpour {: , ...} {: , ...} vpub_old vpub_new */ - - //RPCTypeCheck(params, boost::assign::list_of(str_type)(obj_type)(obj_type)(int_type)(int_type)); + + LOCK(cs_main); CTransaction tx; if (!DecodeHexTx(tx, params[0].get_str())) @@ -2371,24 +2426,61 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp) std::vector vpourin; std::vector vpourout; - /* + uint256 anchor; + BOOST_FOREACH(const Pair& s, inputs) { - // TODO + CDataStream ssData(ParseHexV(s.name_, "bucket"), SER_NETWORK, PROTOCOL_VERSION); + uint64_t value; + std::vector rho; + std::vector r; + + ssData >> value; + ssData >> rho; + ssData >> r; + + std::vector a_sk; + std::string sk_enc; + + { + CDataStream ssData2(ParseHexV(s.value_, "zcsecretkey"), SER_NETWORK, PROTOCOL_VERSION); + try { + ssData2 >> a_sk; + ssData2 >> sk_enc; + } catch(const std::exception &) { + throw runtime_error( + "zcsecretkey could not be decoded" + ); + } + } + + libzerocash::PrivateAddress zcsecretkey(a_sk, sk_enc); + libzerocash::Address zcaddress(zcsecretkey); + libzerocash::Coin input_coin(zcaddress.getPublicAddress(), value, rho, r); + + std::vector commitment_v = input_coin.getCoinCommitment().getCommitmentValue(); + uint256 commitment = uint256(commitment_v); + + libsnark::merkle_authentication_path path(INCREMENTAL_MERKLE_TREE_DEPTH); + size_t path_index = 0; + assert(pwalletMain != NULL); + if (!pwalletMain->WitnessBucketCommitment(commitment, path, path_index, anchor)) { + throw std::runtime_error("Couldn't find bucket in the blockchain"); + } + + vpourin.push_back(PourInput(input_coin, zcaddress, path_index, path)); } - */ - // TODO - vpourin.push_back(PourInput(INCREMENTAL_MERKLE_TREE_DEPTH)); - vpourin.push_back(PourInput(INCREMENTAL_MERKLE_TREE_DEPTH)); + while (vpourin.size() < 2) { + vpourin.push_back(PourInput(INCREMENTAL_MERKLE_TREE_DEPTH)); + } BOOST_FOREACH(const Pair& s, outputs) { libzerocash::PublicAddress addrTo; { - vector decoded(ParseHex(s.name_)); - CDataStream ssData(decoded, SER_NETWORK, PROTOCOL_VERSION); + CDataStream ssData(ParseHexV(s.name_, "to_address"), SER_NETWORK, PROTOCOL_VERSION); std::vector pubAddressSecret; std::string encryptionPublicKey; @@ -2415,7 +2507,6 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp) throw runtime_error("unsupported"); } - uint256 anchor; // TODO CScript scriptPubKey; CPourTx pourtx(*pzerocashParams, scriptPubKey, @@ -2425,6 +2516,8 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp) vpub_old, vpub_new); + assert(pourtx.Verify(*pzerocashParams)); + CMutableTransaction mtx(tx); mtx.nVersion = 2; mtx.vpour.push_back(pourtx); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f412e471e..d9fb67047 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1051,6 +1051,68 @@ bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb) return pwalletdb->WriteTx(GetHash(), *this); } +bool CWallet::WitnessBucketCommitment(uint256 &commitment, + libsnark::merkle_authentication_path& path, + size_t &path_index, + uint256 &final_anchor) +{ + bool res = false; + std::vector commitment_index; + + CBlockIndex* pindex = chainActive.Genesis(); + libzerocash::IncrementalMerkleTree tree(INCREMENTAL_MERKLE_TREE_DEPTH); + uint256 current_anchor; + + while (pindex) { + CBlock block; + ReadBlockFromDisk(block, pindex); + + BOOST_FOREACH(const CTransaction& tx, block.vtx) + { + BOOST_FOREACH(const CPourTx& pour, tx.vpour) + { + BOOST_FOREACH(const uint256 &bucket_commitment, pour.commitments) + { + std::vector commitment_bv(ZC_CM_SIZE * 8); + std::vector index; + std::vector commitment_value(bucket_commitment.begin(), bucket_commitment.end()); + libzerocash::convertBytesVectorToVector(commitment_value, commitment_bv); + tree.insertElement(commitment_bv, index); + + if (bucket_commitment == commitment) { + // We've found it! Now, we construct a witness. + res = true; + tree.prune(); + commitment_index = index; + } + } + } + } + + { + std::vector newrt_v(32); + tree.getRootValue(newrt_v); + current_anchor = uint256(newrt_v); + } + + // Consistency check: we should be able to find the current tree + // in our CCoins view. + libzerocash::IncrementalMerkleTree dummy_tree(INCREMENTAL_MERKLE_TREE_DEPTH); + assert(pcoinsTip->GetAnchorAt(current_anchor, dummy_tree)); + + pindex = chainActive.Next(pindex); + } + + if (res) { + assert(tree.getWitness(commitment_index, path)); + } + + path_index = libzerocash::convertVectorToInt(commitment_index); + final_anchor = current_anchor; + + return res; +} + /** * Scan the block chain (starting in pindexStart) for transactions * from or to us. If fUpdate is true, found transactions that already diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index b0da92cfd..c5385c785 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -616,6 +616,7 @@ public: void SyncTransaction(const CTransaction& tx, const CBlock* pblock); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); void EraseFromWallet(const uint256 &hash); + bool WitnessBucketCommitment(uint256 &commitment, libsnark::merkle_authentication_path& path, size_t &path_index, uint256 &final_anchor); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime);