Merge pull request #625 from Electric-Coin-Company/zc.v0.11.2.ticketXXX-calgary-implementation.2
Calgary Design Foundations
This commit is contained in:
commit
6f466aea69
|
@ -6,7 +6,7 @@ SDK_PATH ?= $(BASEDIR)/SDKs
|
|||
NO_QT ?=
|
||||
NO_WALLET ?=
|
||||
NO_UPNP ?=
|
||||
FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources
|
||||
FALLBACK_DOWNLOAD_PATH ?= https://z.cash/depends-sources
|
||||
|
||||
BUILD = $(shell ./config.guess)
|
||||
HOST ?= $(BUILD)
|
||||
|
|
|
@ -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=c758b1f2b3372fb0e228442745668d0498a183cd0a4bcc423271e4ff3ddde85e
|
||||
$(package)_git_commit=69df6c95d97a1f1ee1fece0a6a7eef7d6a577dbc
|
||||
$(package)_sha256_hash=1364a739751bcdda86cfd66d3d019844d116c374d7a7634bfb3e1a47c085f3c0
|
||||
$(package)_git_commit=dd5db5815be70f0e4895784cc905df6f1c73cb17
|
||||
|
||||
$(package)_dependencies=libsnark crypto++ openssl boost libgmp
|
||||
$(package)_patches=
|
||||
|
|
|
@ -27,6 +27,8 @@ testScripts=(
|
|||
'merkle_blocks.py'
|
||||
'signrawtransactions.py'
|
||||
'walletbackup.py'
|
||||
'zcpour.py'
|
||||
'zcpourdoublespend.py'
|
||||
);
|
||||
testScriptsExt=(
|
||||
'bipdersig-p2p.py'
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#
|
||||
ZCASH_LOAD_TIMEOUT=500
|
||||
DATADIR="@abs_top_builddir@/.zcash"
|
||||
rm -rf "$DATADIR"
|
||||
mkdir -p "$DATADIR"/regtest
|
||||
|
@ -14,7 +15,7 @@ PORT=`expr 10000 + $$ % 55536`
|
|||
BITCOIND=$!
|
||||
|
||||
#Install a watchdog.
|
||||
(sleep 10 && kill -0 $WAITER 2>/dev/null && kill -9 $BITCOIND $$)&
|
||||
(sleep "$ZCASH_LOAD_TIMEOUT" && kill -0 $WAITER 2>/dev/null && kill -9 $BITCOIND $$)&
|
||||
wait $WAITER
|
||||
|
||||
if [ -n "$TIMEOUT" ]; then
|
||||
|
@ -25,7 +26,7 @@ else
|
|||
RETURN=$?
|
||||
fi
|
||||
|
||||
(sleep 15 && kill -0 $BITCOIND 2>/dev/null && kill -9 $BITCOIND $$)&
|
||||
(sleep "$ZCASH_LOAD_TIMEOUT" && kill -0 $BITCOIND 2>/dev/null && kill -9 $BITCOIND $$)&
|
||||
kill $BITCOIND && wait $BITCOIND
|
||||
|
||||
# timeout returns 124 on timeout, otherwise the return value of the child
|
||||
|
|
|
@ -49,7 +49,7 @@ except ImportError:
|
|||
|
||||
USER_AGENT = "AuthServiceProxy/0.1"
|
||||
|
||||
HTTP_TIMEOUT = 30
|
||||
HTTP_TIMEOUT = 600
|
||||
|
||||
log = logging.getLogger("BitcoinRPC")
|
||||
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/env python2
|
||||
|
||||
#
|
||||
# Test Pour semantics
|
||||
#
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
from decimal import Decimal
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
class PourTxTest(BitcoinTestFramework):
|
||||
def setup_network(self):
|
||||
# Start with split network:
|
||||
return super(PourTxTest, self).setup_network(True)
|
||||
|
||||
def send_pours_around(self):
|
||||
zckeypair = self.nodes[1].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)
|
||||
|
||||
print "Syncing blocks..."
|
||||
connect_nodes(self.nodes[1], 0)
|
||||
sync_blocks(self.nodes[0:2])
|
||||
|
||||
print "Done!"
|
||||
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
|
||||
for i in range(4):
|
||||
assert_equal(self.nodes[i].getbalance(), starting_balance)
|
||||
self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
|
||||
|
||||
# Generate zcaddress keypairs
|
||||
zckeypair1 = self.nodes[0].zcrawkeygen()
|
||||
zcsecretkey1 = zckeypair1["zcsecretkey"]
|
||||
zcaddress1 = zckeypair1["zcaddress"]
|
||||
|
||||
zckeypair2 = self.nodes[0].zcrawkeygen()
|
||||
zcsecretkey2 = zckeypair2["zcsecretkey"]
|
||||
zcaddress2 = zckeypair2["zcaddress"]
|
||||
|
||||
self.nodes[0].move("", "foo", 1220)
|
||||
self.nodes[0].move("", "bar", 30)
|
||||
assert_equal(self.nodes[0].getbalance(""), 0)
|
||||
|
||||
change_address = self.nodes[0].getnewaddress("foo")
|
||||
|
||||
# Pour some of our money into this address
|
||||
(total_in, inputs) = gather_inputs(self.nodes[0], 1210)
|
||||
outputs = {}
|
||||
outputs[change_address] = 78
|
||||
rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
|
||||
|
||||
pour_inputs = {}
|
||||
pour_outputs = {}
|
||||
pour_outputs[zcaddress1] = 100
|
||||
pour_outputs[zcaddress2] = 800
|
||||
exception_triggered = False
|
||||
try:
|
||||
pour_result = self.nodes[0].zcrawpour(rawtx, pour_inputs, pour_outputs, 0, 0)
|
||||
except JSONRPCException:
|
||||
exception_triggered = True
|
||||
|
||||
# We expect it to fail; the pour's balance equation isn't adding up.
|
||||
assert_equal(exception_triggered, True)
|
||||
|
||||
pour_outputs[zcaddress1] = 370
|
||||
pour_result = self.nodes[0].zcrawpour(rawtx, pour_inputs, pour_outputs, 1200, 30)
|
||||
# This should succeed to construct a pour: the math adds up!
|
||||
|
||||
signed_tx_pour = self.nodes[0].signrawtransaction(pour_result["rawtxn"])
|
||||
|
||||
print signed_tx_pour
|
||||
|
||||
self.nodes[0].sendrawtransaction(signed_tx_pour["hex"])
|
||||
|
||||
self.send_pours_around()
|
||||
|
||||
if __name__ == '__main__':
|
||||
PourTxTest().main()
|
|
@ -0,0 +1,135 @@
|
|||
#!/usr/bin/env python2
|
||||
|
||||
#
|
||||
# Tests a Pour double-spend and a subsequent reorg.
|
||||
#
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import *
|
||||
from decimal import Decimal
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
class PourTxTest(BitcoinTestFramework):
|
||||
def setup_network(self):
|
||||
# Start with split network:
|
||||
return super(PourTxTest, self).setup_network(True)
|
||||
|
||||
def expect_cannot_pour(self, node, txn):
|
||||
exception_triggered = False
|
||||
|
||||
try:
|
||||
node.sendrawtransaction(txn)
|
||||
except JSONRPCException:
|
||||
exception_triggered = True
|
||||
|
||||
assert_equal(exception_triggered, True)
|
||||
|
||||
def run_test(self):
|
||||
# All nodes should start with 1,250 BTC:
|
||||
starting_balance = 1250
|
||||
for i in range(4):
|
||||
assert_equal(self.nodes[i].getbalance(), starting_balance)
|
||||
self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress!
|
||||
|
||||
# Generate zcaddress keypairs
|
||||
zckeypair = self.nodes[0].zcrawkeygen()
|
||||
zcsecretkey = zckeypair["zcsecretkey"]
|
||||
zcaddress = zckeypair["zcaddress"]
|
||||
|
||||
pool = [0, 1, 2, 3]
|
||||
for i in range(4):
|
||||
(total_in, inputs) = gather_inputs(self.nodes[i], 50)
|
||||
pool[i] = self.nodes[i].createrawtransaction(inputs, {})
|
||||
pool[i] = self.nodes[i].zcrawpour(pool[i], {}, {zcaddress:49.9}, 50, 0.1)
|
||||
signed = self.nodes[i].signrawtransaction(pool[i]["rawtxn"])
|
||||
self.nodes[0].sendrawtransaction(signed["hex"])
|
||||
self.nodes[0].generate(1)
|
||||
self.nodes[1].sendrawtransaction(signed["hex"])
|
||||
self.nodes[1].generate(1)
|
||||
pool[i] = pool[i]["encryptedbucket1"]
|
||||
|
||||
# Confirm that the protects have taken place
|
||||
for i in range(4):
|
||||
enc_bucket = pool[i]
|
||||
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, enc_bucket)
|
||||
assert_equal(receive_result["exists"], True)
|
||||
pool[i] = receive_result["bucket"]
|
||||
|
||||
# Extra confirmation on Node 1
|
||||
receive_result = self.nodes[1].zcrawreceive(zcsecretkey, enc_bucket)
|
||||
assert_equal(receive_result["exists"], True)
|
||||
|
||||
blank_tx = self.nodes[0].createrawtransaction([], {})
|
||||
# Create pour {A, B}->{*}
|
||||
pour_AB = self.nodes[0].zcrawpour(blank_tx,
|
||||
{pool[0] : zcsecretkey, pool[1] : zcsecretkey},
|
||||
{zcaddress:(49.9*2)-0.1},
|
||||
0, 0.1)
|
||||
|
||||
# Create pour {B, C}->{*}
|
||||
pour_BC = self.nodes[0].zcrawpour(blank_tx,
|
||||
{pool[1] : zcsecretkey, pool[2] : zcsecretkey},
|
||||
{zcaddress:(49.9*2)-0.1},
|
||||
0, 0.1)
|
||||
|
||||
# Create pour {C, D}->{*}
|
||||
pour_CD = self.nodes[0].zcrawpour(blank_tx,
|
||||
{pool[2] : zcsecretkey, pool[3] : zcsecretkey},
|
||||
{zcaddress:(49.9*2)-0.1},
|
||||
0, 0.1)
|
||||
|
||||
# Create pour {A, D}->{*}
|
||||
pour_AD = self.nodes[0].zcrawpour(blank_tx,
|
||||
{pool[0] : zcsecretkey, pool[3] : zcsecretkey},
|
||||
{zcaddress:(49.9*2)-0.1},
|
||||
0, 0.1)
|
||||
|
||||
# (a) Node 1 will spend pour AB, then attempt to
|
||||
# double-spend it with BC. It should fail before and
|
||||
# after Node 1 mines blocks.
|
||||
#
|
||||
# (b) Then, Node 2 will spend BC, and mine 5 blocks.
|
||||
# Node 1 connects, and AB will be reorg'd from the chain.
|
||||
# Any attempts to spend AB or CD should fail for
|
||||
# both nodes.
|
||||
#
|
||||
# (c) Then, Node 1 will spend AD, which should work
|
||||
# because the previous spend for A (AB) is considered
|
||||
# invalid.
|
||||
|
||||
# (a)
|
||||
|
||||
self.nodes[0].sendrawtransaction(pour_AB["rawtxn"])
|
||||
|
||||
self.expect_cannot_pour(self.nodes[0], pour_BC["rawtxn"])
|
||||
|
||||
# Generate a block
|
||||
self.nodes[0].generate(1)
|
||||
|
||||
self.expect_cannot_pour(self.nodes[0], pour_BC["rawtxn"])
|
||||
|
||||
# (b)
|
||||
self.nodes[1].sendrawtransaction(pour_BC["rawtxn"])
|
||||
self.nodes[1].generate(5)
|
||||
|
||||
# Connect the two nodes
|
||||
|
||||
connect_nodes(self.nodes[1], 0)
|
||||
sync_blocks(self.nodes[0:2])
|
||||
|
||||
# AB, BC, CD should all be impossible to spend for each node.
|
||||
self.expect_cannot_pour(self.nodes[0], pour_AB["rawtxn"])
|
||||
self.expect_cannot_pour(self.nodes[0], pour_CD["rawtxn"])
|
||||
|
||||
self.expect_cannot_pour(self.nodes[1], pour_AB["rawtxn"])
|
||||
self.expect_cannot_pour(self.nodes[1], pour_CD["rawtxn"])
|
||||
|
||||
# (c)
|
||||
|
||||
self.nodes[0].sendrawtransaction(pour_AD["rawtxn"])
|
||||
self.nodes[0].generate(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
PourTxTest().main()
|
|
@ -140,11 +140,12 @@ public:
|
|||
consensus.nMajorityEnforceBlockUpgrade = 51;
|
||||
consensus.nMajorityRejectBlockOutdated = 75;
|
||||
consensus.nMajorityWindow = 100;
|
||||
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||
consensus.fPowAllowMinDifficultyBlocks = true;
|
||||
pchMessageStart[0] = 0x0b;
|
||||
pchMessageStart[1] = 0x11;
|
||||
pchMessageStart[2] = 0x09;
|
||||
pchMessageStart[3] = 0x07;
|
||||
pchMessageStart[0] = 0x0c;
|
||||
pchMessageStart[1] = 0x12;
|
||||
pchMessageStart[2] = 0x99;
|
||||
pchMessageStart[3] = 0x17;
|
||||
vAlertPubKey = ParseHex("04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a");
|
||||
nDefaultPort = 18333;
|
||||
nMinerThreads = 0;
|
||||
|
@ -153,16 +154,18 @@ public:
|
|||
|
||||
//! Modify the testnet genesis block so the timestamp is valid for a later start.
|
||||
genesis.nTime = 1296688602;
|
||||
genesis.nNonce = 414098458;
|
||||
genesis.nBits = 0x207fffff;
|
||||
genesis.nNonce = 2;
|
||||
consensus.hashGenesisBlock = genesis.GetHash();
|
||||
assert(consensus.hashGenesisBlock == uint256S("0x000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"));
|
||||
assert(consensus.hashGenesisBlock == uint256S("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"));
|
||||
|
||||
vFixedSeeds.clear();
|
||||
vSeeds.clear();
|
||||
vSeeds.push_back(CDNSSeedData("alexykot.me", "testnet-seed.alexykot.me"));
|
||||
vSeeds.push_back(CDNSSeedData("bitcoin.petertodd.org", "testnet-seed.bitcoin.petertodd.org"));
|
||||
vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me"));
|
||||
vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de"));
|
||||
// TODO: set up bootstrapping
|
||||
//vSeeds.push_back(CDNSSeedData("alexykot.me", "testnet-seed.alexykot.me"));
|
||||
//vSeeds.push_back(CDNSSeedData("bitcoin.petertodd.org", "testnet-seed.bitcoin.petertodd.org"));
|
||||
//vSeeds.push_back(CDNSSeedData("bluematt.me", "testnet-seed.bluematt.me"));
|
||||
//vSeeds.push_back(CDNSSeedData("bitcoin.schildbach.de", "testnet-seed.bitcoin.schildbach.de"));
|
||||
|
||||
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
|
||||
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
|
||||
|
@ -181,10 +184,10 @@ public:
|
|||
|
||||
checkpointData = (Checkpoints::CCheckpointData) {
|
||||
boost::assign::map_list_of
|
||||
( 546, uint256S("000000002a936ca763904c3c35fce2f3556c559c0214345d31b1bcebf76acb70")),
|
||||
1337966069,
|
||||
1488,
|
||||
300
|
||||
( 0, consensus.hashGenesisBlock),
|
||||
genesis.nTime,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
}
|
||||
|
|
205
src/coins.cpp
205
src/coins.cpp
|
@ -40,20 +40,34 @@ bool CCoins::Spend(uint32_t nPos)
|
|||
Cleanup();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsView::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const { return false; }
|
||||
bool CCoinsView::GetSerial(const uint256 &serial) const { return false; }
|
||||
bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; }
|
||||
bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; }
|
||||
uint256 CCoinsView::GetBestBlock() const { return uint256(); }
|
||||
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
|
||||
uint256 CCoinsView::GetBestAnchor() const { return uint256(); };
|
||||
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials) { return false; }
|
||||
bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
|
||||
|
||||
|
||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
||||
|
||||
bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); }
|
||||
bool CCoinsViewBacked::GetSerial(const uint256 &serial) const { return base->GetSerial(serial); }
|
||||
bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); }
|
||||
bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); }
|
||||
uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
|
||||
uint256 CCoinsViewBacked::GetBestAnchor() const { return base->GetBestAnchor(); }
|
||||
void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
|
||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return base->BatchWrite(mapCoins, hashBlock); }
|
||||
bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors, mapSerials); }
|
||||
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); }
|
||||
|
||||
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
|
||||
|
@ -87,6 +101,90 @@ CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const {
|
||||
CAnchorsMap::const_iterator it = cacheAnchors.find(rt);
|
||||
if (it != cacheAnchors.end()) {
|
||||
if (it->second.entered) {
|
||||
tree.setTo(it->second.tree);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!base->GetAnchorAt(rt, tree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(rt, CAnchorsCacheEntry())).first;
|
||||
ret->second.entered = true;
|
||||
ret->second.tree.setTo(tree);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::GetSerial(const uint256 &serial) const {
|
||||
CSerialsMap::iterator it = cacheSerials.find(serial);
|
||||
if (it != cacheSerials.end())
|
||||
return it->second.entered;
|
||||
|
||||
CSerialsCacheEntry entry;
|
||||
bool tmp = base->GetSerial(serial);
|
||||
entry.entered = tmp;
|
||||
|
||||
cacheSerials.insert(std::make_pair(serial, entry));
|
||||
|
||||
// TODO: cache usage
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void CCoinsViewCache::PushAnchor(const libzerocash::IncrementalMerkleTree &tree) {
|
||||
std::vector<unsigned char> newrt_v(32);
|
||||
tree.getRootValue(newrt_v);
|
||||
uint256 newrt(newrt_v);
|
||||
|
||||
auto currentRoot = GetBestAnchor();
|
||||
|
||||
// We don't want to overwrite an anchor we already have.
|
||||
// This occurs when a block doesn't modify mapAnchors at all,
|
||||
// because there are no pours. We could get around this a
|
||||
// different way (make all blocks modify mapAnchors somehow)
|
||||
// but this is simpler to reason about.
|
||||
if (currentRoot != newrt) {
|
||||
CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(newrt, CAnchorsCacheEntry())).first;
|
||||
|
||||
ret->second.entered = true;
|
||||
ret->second.tree.setTo(tree);
|
||||
ret->second.flags = CAnchorsCacheEntry::DIRTY;
|
||||
|
||||
hashAnchor = newrt;
|
||||
}
|
||||
}
|
||||
|
||||
void CCoinsViewCache::PopAnchor(const uint256 &newrt) {
|
||||
auto currentRoot = GetBestAnchor();
|
||||
|
||||
// Blocks might not change the commitment tree, in which
|
||||
// case restoring the "old" anchor during a reorg must
|
||||
// have no effect.
|
||||
if (currentRoot != newrt) {
|
||||
CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(currentRoot, CAnchorsCacheEntry())).first;
|
||||
|
||||
ret->second.entered = false;
|
||||
ret->second.flags = CAnchorsCacheEntry::DIRTY;
|
||||
|
||||
hashAnchor = newrt;
|
||||
}
|
||||
}
|
||||
|
||||
void CCoinsViewCache::SetSerial(const uint256 &serial, bool spent) {
|
||||
std::pair<CSerialsMap::iterator, bool> ret = cacheSerials.insert(std::make_pair(serial, CSerialsCacheEntry()));
|
||||
ret.first->second.entered = spent;
|
||||
ret.first->second.flags |= CSerialsCacheEntry::DIRTY;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
|
||||
CCoinsMap::const_iterator it = FetchCoins(txid);
|
||||
if (it != cacheCoins.end()) {
|
||||
|
@ -141,11 +239,22 @@ uint256 CCoinsViewCache::GetBestBlock() const {
|
|||
return hashBlock;
|
||||
}
|
||||
|
||||
|
||||
uint256 CCoinsViewCache::GetBestAnchor() const {
|
||||
if (hashAnchor.IsNull())
|
||||
hashAnchor = base->GetBestAnchor();
|
||||
return hashAnchor;
|
||||
}
|
||||
|
||||
void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
|
||||
hashBlock = hashBlockIn;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn) {
|
||||
bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlockIn,
|
||||
const uint256 &hashAnchorIn,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials) {
|
||||
assert(!hasModifier);
|
||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
|
@ -181,13 +290,73 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
|||
CCoinsMap::iterator itOld = it++;
|
||||
mapCoins.erase(itOld);
|
||||
}
|
||||
|
||||
for (CAnchorsMap::iterator child_it = mapAnchors.begin(); child_it != mapAnchors.end();)
|
||||
{
|
||||
if (child_it->second.flags & CAnchorsCacheEntry::DIRTY) {
|
||||
CAnchorsMap::iterator parent_it = cacheAnchors.find(child_it->first);
|
||||
|
||||
if (parent_it == cacheAnchors.end()) {
|
||||
if (child_it->second.entered) {
|
||||
// Parent doesn't have an entry, but child has a new commitment root.
|
||||
|
||||
CAnchorsCacheEntry& entry = cacheAnchors[child_it->first];
|
||||
entry.entered = true;
|
||||
entry.tree.setTo(child_it->second.tree);
|
||||
entry.flags = CAnchorsCacheEntry::DIRTY;
|
||||
|
||||
// TODO: cache usage
|
||||
}
|
||||
} else {
|
||||
if (parent_it->second.entered != child_it->second.entered) {
|
||||
// The parent may have removed the entry.
|
||||
parent_it->second.entered = child_it->second.entered;
|
||||
parent_it->second.flags |= CAnchorsCacheEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CAnchorsMap::iterator itOld = child_it++;
|
||||
mapAnchors.erase(itOld);
|
||||
}
|
||||
|
||||
for (CSerialsMap::iterator child_it = mapSerials.begin(); child_it != mapSerials.end();)
|
||||
{
|
||||
if (child_it->second.flags & CSerialsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
CSerialsMap::iterator parent_it = cacheSerials.find(child_it->first);
|
||||
|
||||
if (parent_it == cacheSerials.end()) {
|
||||
if (child_it->second.entered) {
|
||||
// Parent doesn't have an entry, but child has a SPENT serial.
|
||||
// Move the spent serial up.
|
||||
|
||||
CSerialsCacheEntry& entry = cacheSerials[child_it->first];
|
||||
entry.entered = true;
|
||||
entry.flags = CSerialsCacheEntry::DIRTY;
|
||||
|
||||
// TODO: cache usage
|
||||
}
|
||||
} else {
|
||||
if (parent_it->second.entered != child_it->second.entered) {
|
||||
parent_it->second.entered = child_it->second.entered;
|
||||
parent_it->second.flags |= CSerialsCacheEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
CSerialsMap::iterator itOld = child_it++;
|
||||
mapSerials.erase(itOld);
|
||||
}
|
||||
|
||||
hashAnchor = hashAnchorIn;
|
||||
hashBlock = hashBlockIn;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::Flush() {
|
||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock);
|
||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashAnchor, cacheAnchors, cacheSerials);
|
||||
cacheCoins.clear();
|
||||
cacheAnchors.clear();
|
||||
cacheSerials.clear();
|
||||
cachedCoinsUsage = 0;
|
||||
return fOk;
|
||||
}
|
||||
|
@ -212,9 +381,35 @@ CAmount CCoinsViewCache::GetValueIn(const CTransaction& tx) const
|
|||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
nResult += GetOutputFor(tx.vin[i]).nValue;
|
||||
|
||||
nResult += tx.GetPourValueIn();
|
||||
|
||||
return nResult;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::HavePourRequirements(const CTransaction& tx) const
|
||||
{
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour)
|
||||
{
|
||||
BOOST_FOREACH(const uint256& serial, pour.serials)
|
||||
{
|
||||
if (GetSerial(serial)) {
|
||||
// If the serial is set, this transaction
|
||||
// double-spends!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
|
|
81
src/coins.h
81
src/coins.h
|
@ -16,6 +16,9 @@
|
|||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "libzerocash/IncrementalMerkleTree.h"
|
||||
|
||||
static const unsigned int INCREMENTAL_MERKLE_TREE_DEPTH = 20;
|
||||
|
||||
/**
|
||||
* Pruned version of CTransaction: only retains metadata and unspent transaction outputs
|
||||
|
@ -295,7 +298,34 @@ struct CCoinsCacheEntry
|
|||
CCoinsCacheEntry() : coins(), flags(0) {}
|
||||
};
|
||||
|
||||
struct CAnchorsCacheEntry
|
||||
{
|
||||
bool entered; // This will be false if the anchor is removed from the cache
|
||||
libzerocash::IncrementalMerkleTree tree; // The tree itself
|
||||
unsigned char flags;
|
||||
|
||||
enum Flags {
|
||||
DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view.
|
||||
};
|
||||
|
||||
CAnchorsCacheEntry() : entered(false), flags(0), tree(INCREMENTAL_MERKLE_TREE_DEPTH) {}
|
||||
};
|
||||
|
||||
struct CSerialsCacheEntry
|
||||
{
|
||||
bool entered; // If the serial is spent or not
|
||||
unsigned char flags;
|
||||
|
||||
enum Flags {
|
||||
DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view.
|
||||
};
|
||||
|
||||
CSerialsCacheEntry() : entered(false), flags(0) {}
|
||||
};
|
||||
|
||||
typedef boost::unordered_map<uint256, CCoinsCacheEntry, CCoinsKeyHasher> CCoinsMap;
|
||||
typedef boost::unordered_map<uint256, CAnchorsCacheEntry, CCoinsKeyHasher> CAnchorsMap;
|
||||
typedef boost::unordered_map<uint256, CSerialsCacheEntry, CCoinsKeyHasher> CSerialsMap;
|
||||
|
||||
struct CCoinsStats
|
||||
{
|
||||
|
@ -315,6 +345,12 @@ struct CCoinsStats
|
|||
class CCoinsView
|
||||
{
|
||||
public:
|
||||
//! Retrieve the tree at a particular anchored root in the chain
|
||||
virtual bool GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const;
|
||||
|
||||
//! Determine whether a serial is spent or not
|
||||
virtual bool GetSerial(const uint256 &serial) const;
|
||||
|
||||
//! Retrieve the CCoins (unspent transaction outputs) for a given txid
|
||||
virtual bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
|
||||
|
@ -325,9 +361,16 @@ public:
|
|||
//! Retrieve the block hash whose state this CCoinsView currently represents
|
||||
virtual uint256 GetBestBlock() const;
|
||||
|
||||
//! Get the current "tip" or the latest anchored tree root in the chain
|
||||
virtual uint256 GetBestAnchor() const;
|
||||
|
||||
//! Do a bulk modification (multiple CCoins changes + BestBlock change).
|
||||
//! The passed mapCoins can be modified.
|
||||
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
|
||||
virtual bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials);
|
||||
|
||||
//! Calculate statistics about the unspent transaction output set
|
||||
virtual bool GetStats(CCoinsStats &stats) const;
|
||||
|
@ -345,11 +388,18 @@ protected:
|
|||
|
||||
public:
|
||||
CCoinsViewBacked(CCoinsView *viewIn);
|
||||
bool GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const;
|
||||
bool GetSerial(const uint256 &serial) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
uint256 GetBestAnchor() const;
|
||||
void SetBackend(CCoinsView &viewIn);
|
||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials);
|
||||
bool GetStats(CCoinsStats &stats) const;
|
||||
};
|
||||
|
||||
|
@ -390,6 +440,9 @@ protected:
|
|||
*/
|
||||
mutable uint256 hashBlock;
|
||||
mutable CCoinsMap cacheCoins;
|
||||
mutable uint256 hashAnchor;
|
||||
mutable CAnchorsMap cacheAnchors;
|
||||
mutable CSerialsMap cacheSerials;
|
||||
|
||||
/* Cached dynamic memory usage for the inner CCoins objects. */
|
||||
mutable size_t cachedCoinsUsage;
|
||||
|
@ -399,11 +452,30 @@ public:
|
|||
~CCoinsViewCache();
|
||||
|
||||
// Standard CCoinsView methods
|
||||
bool GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const;
|
||||
bool GetSerial(const uint256 &serial) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
uint256 GetBestAnchor() const;
|
||||
void SetBestBlock(const uint256 &hashBlock);
|
||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials);
|
||||
|
||||
|
||||
// Adds the tree to mapAnchors and sets the current commitment
|
||||
// root to this root.
|
||||
void PushAnchor(const libzerocash::IncrementalMerkleTree &tree);
|
||||
|
||||
// Removes the current commitment root from mapAnchors and sets
|
||||
// the new current root.
|
||||
void PopAnchor(const uint256 &rt);
|
||||
|
||||
// Marks a serial as spent or not.
|
||||
void SetSerial(const uint256 &serial, bool spent);
|
||||
|
||||
/**
|
||||
* Return a pointer to CCoins in the cache, or NULL if not found. This is
|
||||
|
@ -445,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;
|
||||
|
||||
|
|
|
@ -30,8 +30,9 @@ private:
|
|||
std::string strRejectReason;
|
||||
unsigned char chRejectCode;
|
||||
bool corruptionPossible;
|
||||
bool pourVerify;
|
||||
public:
|
||||
CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false) {}
|
||||
CValidationState() : mode(MODE_VALID), nDoS(0), chRejectCode(0), corruptionPossible(false), pourVerify(true) {}
|
||||
bool DoS(int level, bool ret = false,
|
||||
unsigned char chRejectCodeIn=0, std::string strRejectReasonIn="",
|
||||
bool corruptionIn=false) {
|
||||
|
@ -44,6 +45,12 @@ public:
|
|||
mode = MODE_INVALID;
|
||||
return ret;
|
||||
}
|
||||
bool SetPerformPourVerification(bool pourVerifyIn) {
|
||||
pourVerify = pourVerifyIn;
|
||||
}
|
||||
bool PerformPourVerification() {
|
||||
return pourVerify;
|
||||
}
|
||||
bool Invalid(bool ret = false,
|
||||
unsigned char _chRejectCode=0, std::string _strRejectReason="") {
|
||||
return DoS(0, ret, _chRejectCode, _strRejectReason);
|
||||
|
|
50
src/init.cpp
50
src/init.cpp
|
@ -47,8 +47,12 @@
|
|||
#include <boost/thread.hpp>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
#include "libsnark/common/profiling.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
libzerocash::ZerocashParams *pzerocashParams = NULL;
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
CWallet* pwalletMain = NULL;
|
||||
#endif
|
||||
|
@ -591,11 +595,52 @@ bool InitSanityCheck(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void ZC_LoadParams()
|
||||
{
|
||||
struct timeval tv_start, tv_end;
|
||||
float elapsed;
|
||||
|
||||
boost::filesystem::path pk_path = ZC_GetParamsDir() / "zc-testnet-public-alpha-proving.key";
|
||||
boost::filesystem::path vk_path = ZC_GetParamsDir() / "zc-testnet-public-alpha-verification.key";
|
||||
|
||||
LogPrintf("Loading proving key from %s\n", pk_path.string().c_str());
|
||||
gettimeofday(&tv_start, 0);
|
||||
libzerocash::ZerocashParams::zerocash_pp::init_public_params();
|
||||
auto pk_loaded = libzerocash::ZerocashParams::LoadProvingKeyFromFile(
|
||||
pk_path.string(),
|
||||
INCREMENTAL_MERKLE_TREE_DEPTH
|
||||
);
|
||||
gettimeofday(&tv_end, 0);
|
||||
elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000);
|
||||
LogPrintf("Loaded proving key in %fs seconds.\n", elapsed);
|
||||
|
||||
|
||||
LogPrintf("Loading verification key from %s\n", vk_path.string().c_str());
|
||||
gettimeofday(&tv_start, 0);
|
||||
auto vk_loaded = libzerocash::ZerocashParams::LoadVerificationKeyFromFile(
|
||||
vk_path.string(),
|
||||
INCREMENTAL_MERKLE_TREE_DEPTH
|
||||
);
|
||||
gettimeofday(&tv_end, 0);
|
||||
elapsed = float(tv_end.tv_sec-tv_start.tv_sec) + (tv_end.tv_usec-tv_start.tv_usec)/float(1000000);
|
||||
LogPrintf("Loaded verification key in %fs seconds.\n", elapsed);
|
||||
|
||||
pzerocashParams = new libzerocash::ZerocashParams(
|
||||
INCREMENTAL_MERKLE_TREE_DEPTH,
|
||||
&pk_loaded,
|
||||
&vk_loaded
|
||||
);
|
||||
}
|
||||
|
||||
/** Initialize bitcoin.
|
||||
* @pre Parameters should be parsed and config file should be read.
|
||||
*/
|
||||
bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||
{
|
||||
// ********************************************************* Step 0: Load zcash params
|
||||
ZC_LoadParams();
|
||||
|
||||
// ********************************************************* Step 1: setup
|
||||
#ifdef _MSC_VER
|
||||
// Turn off Microsoft heap dump noise
|
||||
|
@ -1223,6 +1268,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||
mempool.ReadFeeEstimates(est_filein);
|
||||
fFeeEstimatesInitialized = true;
|
||||
|
||||
// These must be disabled for now, they are buggy and we probably don't
|
||||
// want any of libsnark's profiling in production anyway.
|
||||
libsnark::inhibit_profiling_info = true;
|
||||
libsnark::inhibit_profiling_counters = true;
|
||||
|
||||
// ********************************************************* Step 8: load wallet
|
||||
#ifdef ENABLE_WALLET
|
||||
if (fDisableWallet) {
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
#include "libzerocash/ZerocashParams.h"
|
||||
|
||||
class CScheduler;
|
||||
class CWallet;
|
||||
|
||||
|
@ -17,6 +19,7 @@ class thread_group;
|
|||
} // namespace boost
|
||||
|
||||
extern CWallet* pwalletMain;
|
||||
extern libzerocash::ZerocashParams* pzerocashParams;
|
||||
|
||||
void StartShutdown();
|
||||
bool ShutdownRequested();
|
||||
|
|
154
src/main.cpp
154
src/main.cpp
|
@ -847,12 +847,16 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
|
|||
bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
||||
{
|
||||
// Basic checks that don't depend on any context
|
||||
if (tx.vin.empty())
|
||||
|
||||
// Transactions can contain empty `vin` and `vout` so long as
|
||||
// `vpour` is non-empty.
|
||||
if (tx.vin.empty() && tx.vpour.empty())
|
||||
return state.DoS(10, error("CheckTransaction(): vin empty"),
|
||||
REJECT_INVALID, "bad-txns-vin-empty");
|
||||
if (tx.vout.empty())
|
||||
if (tx.vout.empty() && tx.vpour.empty())
|
||||
return state.DoS(10, error("CheckTransaction(): vout empty"),
|
||||
REJECT_INVALID, "bad-txns-vout-empty");
|
||||
|
||||
// Size limits
|
||||
if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE)
|
||||
return state.DoS(100, error("CheckTransaction(): size limits failed"),
|
||||
|
@ -874,6 +878,32 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
|||
REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
}
|
||||
|
||||
// Ensure that pour values are well-formed
|
||||
BOOST_FOREACH(const CPourTx& pour, tx.vpour)
|
||||
{
|
||||
if (pour.vpub_old < 0)
|
||||
return state.DoS(100, error("CheckTransaction(): pour.vpub_old negative"),
|
||||
REJECT_INVALID, "bad-txns-vpub_old-negative");
|
||||
|
||||
if (pour.vpub_new < 0)
|
||||
return state.DoS(100, error("CheckTransaction(): pour.vpub_new negative"),
|
||||
REJECT_INVALID, "bad-txns-vpub_new-negative");
|
||||
|
||||
if (pour.vpub_old > MAX_MONEY)
|
||||
return state.DoS(100, error("CheckTransaction(): pour.vpub_old too high"),
|
||||
REJECT_INVALID, "bad-txns-vpub_old-toolarge");
|
||||
|
||||
if (pour.vpub_new > MAX_MONEY)
|
||||
return state.DoS(100, error("CheckTransaction(): pour.vpub_new too high"),
|
||||
REJECT_INVALID, "bad-txns-vpub_new-toolarge");
|
||||
|
||||
nValueOut += pour.vpub_new;
|
||||
if (!MoneyRange(nValueOut))
|
||||
return state.DoS(100, error("CheckTransaction(): txout total out of range"),
|
||||
REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
}
|
||||
|
||||
|
||||
// Check for duplicate inputs
|
||||
set<COutPoint> vInOutPoints;
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
|
@ -884,8 +914,27 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
|||
vInOutPoints.insert(txin.prevout);
|
||||
}
|
||||
|
||||
// Check for duplicate pour serials in this transaction
|
||||
set<uint256> vPourSerials;
|
||||
BOOST_FOREACH(const CPourTx& pour, tx.vpour)
|
||||
{
|
||||
BOOST_FOREACH(const uint256& serial, pour.serials)
|
||||
{
|
||||
if (vPourSerials.count(serial))
|
||||
return state.DoS(100, error("CheckTransaction(): duplicate serials"),
|
||||
REJECT_INVALID, "bad-pours-serials-duplicate");
|
||||
|
||||
vPourSerials.insert(serial);
|
||||
}
|
||||
}
|
||||
|
||||
if (tx.IsCoinBase())
|
||||
{
|
||||
// There should be no pours in a coinbase transaction
|
||||
if (tx.vpour.size() > 0)
|
||||
return state.DoS(100, error("CheckTransaction(): coinbase has pours"),
|
||||
REJECT_INVALID, "bad-cb-has-pours");
|
||||
|
||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
||||
return state.DoS(100, error("CheckTransaction(): coinbase script size"),
|
||||
REJECT_INVALID, "bad-cb-length");
|
||||
|
@ -896,6 +945,17 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
|||
if (txin.prevout.IsNull())
|
||||
return state.DoS(10, error("CheckTransaction(): prevout is null"),
|
||||
REJECT_INVALID, "bad-txns-prevout-null");
|
||||
|
||||
// Ensure that zk-SNARKs verify
|
||||
|
||||
if (state.PerformPourVerification()) {
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
if (!pour.Verify(*pzerocashParams)) {
|
||||
return state.DoS(100, error("CheckTransaction(): pour does not verify"),
|
||||
REJECT_INVALID, "bad-txns-pour-verification-failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -976,6 +1036,14 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
|
|||
return false;
|
||||
}
|
||||
}
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
BOOST_FOREACH(const uint256 &serial, pour.serials) {
|
||||
if (pool.mapSerials.count(serial))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1008,6 +1076,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();
|
||||
|
||||
|
@ -1426,6 +1499,13 @@ void UpdateCoins(const CTransaction& tx, CValidationState &state, CCoinsViewCach
|
|||
}
|
||||
}
|
||||
|
||||
// spend serials
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
BOOST_FOREACH(const uint256 &serial, pour.serials) {
|
||||
inputs.SetSerial(serial, true);
|
||||
}
|
||||
}
|
||||
|
||||
// add outputs
|
||||
inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight);
|
||||
}
|
||||
|
@ -1456,6 +1536,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;
|
||||
|
@ -1484,6 +1568,11 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
|
|||
|
||||
}
|
||||
|
||||
nValueIn += tx.GetPourValueIn();
|
||||
if (!MoneyRange(nValueIn))
|
||||
return state.DoS(100, error("CheckInputs(): vpub_old values out of range"),
|
||||
REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
||||
|
||||
if (nValueIn < tx.GetValueOut())
|
||||
return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s)",
|
||||
tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())),
|
||||
|
@ -1698,6 +1787,13 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex
|
|||
outs->Clear();
|
||||
}
|
||||
|
||||
// unspend serials
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
BOOST_FOREACH(const uint256 &serial, pour.serials) {
|
||||
view.SetSerial(serial, false);
|
||||
}
|
||||
}
|
||||
|
||||
// restore inputs
|
||||
if (i > 0) { // not coinbases
|
||||
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
|
||||
|
@ -1712,6 +1808,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());
|
||||
|
||||
|
@ -1899,6 +1998,25 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
|||
std::vector<std::pair<uint256, CDiskTxPos> > 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<unsigned char> 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];
|
||||
|
@ -1915,6 +2033,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;
|
||||
|
@ -1940,9 +2063,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<bool> index;
|
||||
std::vector<unsigned char> commitment_value(bucket_commitment.begin(), bucket_commitment.end());
|
||||
std::vector<bool> 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);
|
||||
|
||||
|
@ -2171,6 +2311,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);
|
||||
|
@ -2179,6 +2320,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;
|
||||
|
@ -2190,6 +2332,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.
|
||||
|
@ -4482,7 +4629,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());
|
||||
|
||||
|
|
|
@ -9,6 +9,87 @@
|
|||
#include "tinyformat.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include "libzerocash/PourProver.h"
|
||||
#include "libzerocash/PourTransaction.h"
|
||||
|
||||
template<std::size_t N>
|
||||
boost::array<std::vector<unsigned char>, N> uint256_to_array(const boost::array<uint256, N>& in) {
|
||||
boost::array<std::vector<unsigned char>, N> result;
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
result[i] = std::vector<unsigned char>(in[i].begin(), in[i].end());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
boost::array<uint256, N> unsigned_char_vector_array_to_uint256_array(const boost::array<std::vector<unsigned char>, N>& in) {
|
||||
boost::array<uint256, N> result;
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
result[i] = uint256(in[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CPourTx::CPourTx(ZerocashParams& params,
|
||||
const CScript& scriptPubKey,
|
||||
const uint256& anchor,
|
||||
const boost::array<PourInput, NUM_POUR_INPUTS>& inputs,
|
||||
const boost::array<PourOutput, NUM_POUR_OUTPUTS>& outputs,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new) : scriptSig(), scriptPubKey(scriptPubKey), vpub_old(vpub_old), vpub_new(vpub_new), anchor(anchor)
|
||||
{
|
||||
uint256 scriptPubKeyHash;
|
||||
{
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
ss << scriptPubKey;
|
||||
scriptPubKeyHash = ss.GetHash();
|
||||
}
|
||||
|
||||
PourTransaction pourtx(params,
|
||||
std::vector<unsigned char>(scriptPubKeyHash.begin(), scriptPubKeyHash.end()),
|
||||
std::vector<unsigned char>(anchor.begin(), anchor.end()),
|
||||
std::vector<PourInput>(inputs.begin(), inputs.end()),
|
||||
std::vector<PourOutput>(outputs.begin(), outputs.end()),
|
||||
vpub_old,
|
||||
vpub_new);
|
||||
|
||||
boost::array<std::vector<unsigned char>, NUM_POUR_INPUTS> serials_bv;
|
||||
boost::array<std::vector<unsigned char>, NUM_POUR_OUTPUTS> commitments_bv;
|
||||
boost::array<std::vector<unsigned char>, NUM_POUR_INPUTS> macs_bv;
|
||||
boost::array<std::string, NUM_POUR_OUTPUTS> ciphertexts_bv;
|
||||
|
||||
proof = pourtx.unpack(serials_bv, commitments_bv, macs_bv, ciphertexts_bv);
|
||||
serials = unsigned_char_vector_array_to_uint256_array(serials_bv);
|
||||
commitments = unsigned_char_vector_array_to_uint256_array(commitments_bv);
|
||||
macs = unsigned_char_vector_array_to_uint256_array(macs_bv);
|
||||
|
||||
ciphertexts = ciphertexts_bv;
|
||||
}
|
||||
|
||||
bool CPourTx::Verify(ZerocashParams& params) const {
|
||||
// Compute the hash of the scriptPubKey.
|
||||
uint256 scriptPubKeyHash;
|
||||
{
|
||||
CHashWriter ss(SER_GETHASH, 0);
|
||||
ss << scriptPubKey;
|
||||
scriptPubKeyHash = ss.GetHash();
|
||||
}
|
||||
|
||||
return PourProver::VerifyProof(
|
||||
params,
|
||||
std::vector<unsigned char>(scriptPubKeyHash.begin(), scriptPubKeyHash.end()),
|
||||
std::vector<unsigned char>(anchor.begin(), anchor.end()),
|
||||
vpub_old,
|
||||
vpub_new,
|
||||
uint256_to_array<NUM_POUR_INPUTS>(serials),
|
||||
uint256_to_array<NUM_POUR_OUTPUTS>(commitments),
|
||||
uint256_to_array<NUM_POUR_INPUTS>(macs),
|
||||
proof
|
||||
);
|
||||
}
|
||||
|
||||
std::string COutPoint::ToString() const
|
||||
{
|
||||
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n);
|
||||
|
@ -60,7 +141,7 @@ std::string CTxOut::ToString() const
|
|||
}
|
||||
|
||||
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
|
||||
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {}
|
||||
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vpour(tx.vpour) {}
|
||||
|
||||
uint256 CMutableTransaction::GetHash() const
|
||||
{
|
||||
|
@ -72,9 +153,9 @@ void CTransaction::UpdateHash() const
|
|||
*const_cast<uint256*>(&hash) = SerializeHash(*this);
|
||||
}
|
||||
|
||||
CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0) { }
|
||||
CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), vpour() { }
|
||||
|
||||
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {
|
||||
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vpour(tx.vpour) {
|
||||
UpdateHash();
|
||||
}
|
||||
|
||||
|
@ -83,6 +164,7 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
|
|||
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
|
||||
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
|
||||
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
|
||||
*const_cast<std::vector<CPourTx>*>(&vpour) = tx.vpour;
|
||||
*const_cast<uint256*>(&hash) = tx.hash;
|
||||
return *this;
|
||||
}
|
||||
|
@ -96,9 +178,33 @@ CAmount CTransaction::GetValueOut() const
|
|||
if (!MoneyRange(it->nValue) || !MoneyRange(nValueOut))
|
||||
throw std::runtime_error("CTransaction::GetValueOut(): value out of range");
|
||||
}
|
||||
|
||||
for (std::vector<CPourTx>::const_iterator it(vpour.begin()); it != vpour.end(); ++it)
|
||||
{
|
||||
// NB: vpub_old "takes" money from the value pool just as outputs do
|
||||
nValueOut += it->vpub_old;
|
||||
|
||||
if (!MoneyRange(it->vpub_old) || !MoneyRange(nValueOut))
|
||||
throw std::runtime_error("CTransaction::GetValueOut(): value out of range");
|
||||
}
|
||||
return nValueOut;
|
||||
}
|
||||
|
||||
CAmount CTransaction::GetPourValueIn() const
|
||||
{
|
||||
CAmount nValue = 0;
|
||||
for (std::vector<CPourTx>::const_iterator it(vpour.begin()); it != vpour.end(); ++it)
|
||||
{
|
||||
// NB: vpub_new "gives" money to the value pool just as inputs do
|
||||
nValue += it->vpub_new;
|
||||
|
||||
if (!MoneyRange(it->vpub_new) || !MoneyRange(nValue))
|
||||
throw std::runtime_error("CTransaction::GetPourValueIn(): value out of range");
|
||||
}
|
||||
|
||||
return nValue;
|
||||
}
|
||||
|
||||
double CTransaction::ComputePriority(double dPriorityInputs, unsigned int nTxSize) const
|
||||
{
|
||||
nTxSize = CalculateModifiedSize(nTxSize);
|
||||
|
|
|
@ -11,6 +11,121 @@
|
|||
#include "serialize.h"
|
||||
#include "uint256.h"
|
||||
|
||||
#include <boost/array.hpp>
|
||||
|
||||
#include "libzerocash/ZerocashParams.h"
|
||||
#include "libzerocash/PourInput.h"
|
||||
#include "libzerocash/PourOutput.h"
|
||||
|
||||
using namespace libzerocash;
|
||||
|
||||
static const unsigned int NUM_POUR_INPUTS = 2;
|
||||
static const unsigned int NUM_POUR_OUTPUTS = 2;
|
||||
|
||||
class CPourTx
|
||||
{
|
||||
public:
|
||||
// These values 'enter from' and 'exit to' the value
|
||||
// pool, respectively.
|
||||
CAmount vpub_old;
|
||||
CAmount vpub_new;
|
||||
|
||||
// These scripts are used to bind a Pour to the outer
|
||||
// transaction it is placed in. The Pour will
|
||||
// authenticate the hash of the scriptPubKey, and the
|
||||
// provided scriptSig with be appended during
|
||||
// transaction verification.
|
||||
CScript scriptPubKey;
|
||||
CScript scriptSig;
|
||||
|
||||
// Pours are always anchored to a root in the bucket
|
||||
// commitment tree at some point in the blockchain
|
||||
// history or in the history of the current
|
||||
// transaction.
|
||||
uint256 anchor;
|
||||
|
||||
// Serials are used to prevent double-spends. They
|
||||
// are derived from the secrets placed in the bucket
|
||||
// and the secret spend-authority key known by the
|
||||
// spender.
|
||||
boost::array<uint256, NUM_POUR_INPUTS> serials;
|
||||
|
||||
// Bucket commitments are introduced into the commitment
|
||||
// tree, blinding the public about the values and
|
||||
// destinations involved in the Pour. The presence of a
|
||||
// commitment in the bucket commitment tree is required
|
||||
// to spend it.
|
||||
boost::array<uint256, NUM_POUR_OUTPUTS> commitments;
|
||||
|
||||
// Ciphertexts
|
||||
// These are encrypted using ECIES. They are used to
|
||||
// transfer metadata and seeds to generate trapdoors
|
||||
// for the recipient to spend the value.
|
||||
boost::array<std::string, NUM_POUR_OUTPUTS> ciphertexts;
|
||||
|
||||
// MACs
|
||||
// The verification of the pour requires these MACs
|
||||
// to be provided as an input.
|
||||
boost::array<uint256, NUM_POUR_INPUTS> macs;
|
||||
|
||||
// Pour proof
|
||||
// This is a zk-SNARK which ensures that this pour is valid.
|
||||
std::string proof;
|
||||
|
||||
CPourTx(): vpub_old(0), vpub_new(0), scriptPubKey(), scriptSig(), anchor(), serials(), commitments(), ciphertexts(), macs(), proof() {
|
||||
|
||||
}
|
||||
|
||||
CPourTx(ZerocashParams& params,
|
||||
const CScript& scriptPubKey,
|
||||
const uint256& rt,
|
||||
const boost::array<PourInput, NUM_POUR_INPUTS>& inputs,
|
||||
const boost::array<PourOutput, NUM_POUR_OUTPUTS>& outputs,
|
||||
CAmount vpub_old,
|
||||
CAmount vpub_new
|
||||
);
|
||||
|
||||
// Verifies that the pour proof is correct.
|
||||
bool Verify(ZerocashParams& params) const;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(vpub_old);
|
||||
READWRITE(vpub_new);
|
||||
READWRITE(scriptPubKey);
|
||||
READWRITE(scriptSig);
|
||||
READWRITE(anchor);
|
||||
READWRITE(serials);
|
||||
READWRITE(commitments);
|
||||
READWRITE(ciphertexts);
|
||||
READWRITE(macs);
|
||||
READWRITE(proof);
|
||||
}
|
||||
|
||||
friend bool operator==(const CPourTx& a, const CPourTx& b)
|
||||
{
|
||||
return (
|
||||
a.vpub_old == b.vpub_old &&
|
||||
a.vpub_new == b.vpub_new &&
|
||||
a.scriptPubKey == b.scriptPubKey &&
|
||||
a.scriptSig == b.scriptSig &&
|
||||
a.anchor == b.anchor &&
|
||||
a.serials == b.serials &&
|
||||
a.commitments == b.commitments &&
|
||||
a.ciphertexts == b.ciphertexts &&
|
||||
a.macs == b.macs &&
|
||||
a.proof == b.proof
|
||||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CPourTx& a, const CPourTx& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
};
|
||||
|
||||
/** An outpoint - a combination of a transaction hash and an index n into its vout */
|
||||
class COutPoint
|
||||
{
|
||||
|
@ -192,6 +307,7 @@ public:
|
|||
const std::vector<CTxIn> vin;
|
||||
const std::vector<CTxOut> vout;
|
||||
const uint32_t nLockTime;
|
||||
const std::vector<CPourTx> vpour;
|
||||
|
||||
/** Construct a CTransaction that qualifies as IsNull() */
|
||||
CTransaction();
|
||||
|
@ -210,6 +326,9 @@ public:
|
|||
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
|
||||
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(*const_cast<std::vector<CPourTx>*>(&vpour));
|
||||
}
|
||||
if (ser_action.ForRead())
|
||||
UpdateHash();
|
||||
}
|
||||
|
@ -227,6 +346,9 @@ public:
|
|||
// GetValueIn() is a method on CCoinsViewCache, because
|
||||
// inputs must be known to compute value in.
|
||||
|
||||
// Return sum of pour vpub_new
|
||||
CAmount GetPourValueIn() const;
|
||||
|
||||
// Compute priority, given priority of inputs and (optionally) tx size
|
||||
double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const;
|
||||
|
||||
|
@ -258,6 +380,7 @@ struct CMutableTransaction
|
|||
std::vector<CTxIn> vin;
|
||||
std::vector<CTxOut> vout;
|
||||
uint32_t nLockTime;
|
||||
std::vector<CPourTx> vpour;
|
||||
|
||||
CMutableTransaction();
|
||||
CMutableTransaction(const CTransaction& tx);
|
||||
|
@ -271,6 +394,9 @@ struct CMutableTransaction
|
|||
READWRITE(vin);
|
||||
READWRITE(vout);
|
||||
READWRITE(nLockTime);
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(vpour);
|
||||
}
|
||||
}
|
||||
|
||||
/** Compute the hash of this CMutableTransaction. This is computed on the
|
||||
|
|
|
@ -91,6 +91,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "estimatepriority", 0 },
|
||||
{ "prioritisetransaction", 1 },
|
||||
{ "prioritisetransaction", 2 },
|
||||
{ "zcrawpour", 1 },
|
||||
{ "zcrawpour", 2 },
|
||||
{ "zcrawpour", 3 },
|
||||
{ "zcrawpour", 4 }
|
||||
};
|
||||
|
||||
class CRPCConvertTable
|
||||
|
|
|
@ -376,6 +376,9 @@ static const CRPCCommand vRPCCommands[] =
|
|||
{ "wallet", "walletlock", &walletlock, true },
|
||||
{ "wallet", "walletpassphrasechange", &walletpassphrasechange, true },
|
||||
{ "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
|
||||
};
|
||||
|
||||
|
|
|
@ -208,6 +208,9 @@ extern json_spirit::Value getblockchaininfo(const json_spirit::Array& params, bo
|
|||
extern json_spirit::Value getnetworkinfo(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHelp);
|
||||
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);
|
||||
|
|
|
@ -1070,6 +1070,26 @@ public:
|
|||
SerializeOutput(s, nOutput, nType, nVersion);
|
||||
// Serialize nLockTime
|
||||
::Serialize(s, txTo.nLockTime, nType, nVersion);
|
||||
|
||||
// Serialize vpour
|
||||
if (txTo.nVersion >= 2) {
|
||||
// TODO:
|
||||
//
|
||||
// SIGHASH_* functions will hash portions of
|
||||
// the transaction for use in signatures. This
|
||||
// keeps the pour cryptographically bound to
|
||||
// the transaction from the perspective of the
|
||||
// inputs (but not from the perspective of the
|
||||
// pour).
|
||||
//
|
||||
// This must be rectified in the future.
|
||||
// See zcash/#529
|
||||
//
|
||||
// It will be necessary to change this API to
|
||||
// be abstract over whether an input script is
|
||||
// being skipped or a pour is being skipped.
|
||||
::Serialize(s, txTo.vpour, nType, nVersion);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/array.hpp>
|
||||
|
||||
class CScript;
|
||||
|
||||
static const unsigned int MAX_SIZE = 0x02000000;
|
||||
|
@ -506,6 +508,13 @@ extern inline unsigned int GetSerializeSize(const CScript& v, int nType, int nVe
|
|||
template<typename Stream> void Serialize(Stream& os, const CScript& v, int nType, int nVersion);
|
||||
template<typename Stream> void Unserialize(Stream& is, CScript& v, int nType, int nVersion);
|
||||
|
||||
/**
|
||||
* array
|
||||
*/
|
||||
template<typename T, std::size_t N> unsigned int GetSerializeSize(const boost::array<T, N> &item, int nType, int nVersion);
|
||||
template<typename Stream, typename T, std::size_t N> void Serialize(Stream& os, const boost::array<T, N>& item, int nType, int nVersion);
|
||||
template<typename Stream, typename T, std::size_t N> void Unserialize(Stream& is, boost::array<T, N>& item, int nType, int nVersion);
|
||||
|
||||
/**
|
||||
* pair
|
||||
*/
|
||||
|
@ -698,6 +707,35 @@ void Unserialize(Stream& is, CScript& v, int nType, int nVersion)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* array
|
||||
*/
|
||||
template<typename T, std::size_t N>
|
||||
unsigned int GetSerializeSize(const boost::array<T, N> &item, int nType, int nVersion)
|
||||
{
|
||||
unsigned int size = 0;
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
size += GetSerializeSize(item[0], nType, nVersion);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
template<typename Stream, typename T, std::size_t N>
|
||||
void Serialize(Stream& os, const boost::array<T, N>& item, int nType, int nVersion)
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
Serialize(os, item[i], nType, nVersion);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Stream, typename T, std::size_t N>
|
||||
void Unserialize(Stream& is, boost::array<T, N>& item, int nType, int nVersion)
|
||||
{
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
Unserialize(is, item[i], nType, nVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pair
|
||||
|
|
|
@ -11,15 +11,50 @@
|
|||
#include <map>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "libzerocash/IncrementalMerkleTree.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
class CCoinsViewTest : public CCoinsView
|
||||
{
|
||||
uint256 hashBestBlock_;
|
||||
uint256 hashBestAnchor_;
|
||||
std::map<uint256, CCoins> map_;
|
||||
std::map<uint256, libzerocash::IncrementalMerkleTree> mapAnchors_;
|
||||
std::map<uint256, bool> mapSerials_;
|
||||
|
||||
public:
|
||||
bool GetAnchorAt(const uint256& rt, libzerocash::IncrementalMerkleTree &tree) const {
|
||||
if (rt.IsNull()) {
|
||||
IncrementalMerkleTree new_tree(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
tree.setTo(new_tree);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<uint256, libzerocash::IncrementalMerkleTree>::const_iterator it = mapAnchors_.find(rt);
|
||||
if (it == mapAnchors_.end()) {
|
||||
return false;
|
||||
} else {
|
||||
tree.setTo(it->second);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetSerial(const uint256 &serial) const
|
||||
{
|
||||
std::map<uint256, bool>::const_iterator it = mapSerials_.find(serial);
|
||||
|
||||
if (it == mapSerials_.end()) {
|
||||
return false;
|
||||
} else {
|
||||
// The map shouldn't contain any false entries.
|
||||
assert(it->second);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
uint256 GetBestAnchor() const { return hashBestAnchor_; }
|
||||
|
||||
bool GetCoins(const uint256& txid, CCoins& coins) const
|
||||
{
|
||||
std::map<uint256, CCoins>::const_iterator it = map_.find(txid);
|
||||
|
@ -42,7 +77,11 @@ public:
|
|||
|
||||
uint256 GetBestBlock() const { return hashBestBlock_; }
|
||||
|
||||
bool BatchWrite(CCoinsMap& mapCoins, const uint256& hashBlock)
|
||||
bool BatchWrite(CCoinsMap& mapCoins,
|
||||
const uint256& hashBlock,
|
||||
const uint256& hashAnchor,
|
||||
CAnchorsMap& mapAnchors,
|
||||
CSerialsMap& mapSerials)
|
||||
{
|
||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
|
||||
map_[it->first] = it->second.coins;
|
||||
|
@ -52,8 +91,30 @@ public:
|
|||
}
|
||||
mapCoins.erase(it++);
|
||||
}
|
||||
for (CAnchorsMap::iterator it = mapAnchors.begin(); it != mapAnchors.end(); ) {
|
||||
if (it->second.entered) {
|
||||
std::map<uint256, libzerocash::IncrementalMerkleTree>::iterator ret =
|
||||
mapAnchors_.insert(std::make_pair(it->first, IncrementalMerkleTree(INCREMENTAL_MERKLE_TREE_DEPTH))).first;
|
||||
|
||||
ret->second.setTo(it->second.tree);
|
||||
} else {
|
||||
mapAnchors_.erase(it->first);
|
||||
}
|
||||
mapAnchors.erase(it++);
|
||||
}
|
||||
for (CSerialsMap::iterator it = mapSerials.begin(); it != mapSerials.end(); ) {
|
||||
if (it->second.entered) {
|
||||
mapSerials_[it->first] = true;
|
||||
} else {
|
||||
mapSerials_.erase(it->first);
|
||||
}
|
||||
mapSerials.erase(it++);
|
||||
}
|
||||
mapCoins.clear();
|
||||
mapAnchors.clear();
|
||||
mapSerials.clear();
|
||||
hashBestBlock_ = hashBlock;
|
||||
hashBestAnchor_ = hashAnchor;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -79,6 +140,196 @@ public:
|
|||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(serials_test)
|
||||
{
|
||||
CCoinsViewTest base;
|
||||
CCoinsViewCacheTest cache(&base);
|
||||
|
||||
uint256 myserial = GetRandHash();
|
||||
|
||||
BOOST_CHECK(!cache.GetSerial(myserial));
|
||||
cache.SetSerial(myserial, true);
|
||||
BOOST_CHECK(cache.GetSerial(myserial));
|
||||
cache.Flush();
|
||||
|
||||
CCoinsViewCacheTest cache2(&base);
|
||||
|
||||
BOOST_CHECK(cache2.GetSerial(myserial));
|
||||
cache2.SetSerial(myserial, false);
|
||||
BOOST_CHECK(!cache2.GetSerial(myserial));
|
||||
cache2.Flush();
|
||||
|
||||
CCoinsViewCacheTest cache3(&base);
|
||||
|
||||
BOOST_CHECK(!cache3.GetSerial(myserial));
|
||||
}
|
||||
|
||||
void appendRandomCommitment(IncrementalMerkleTree &tree)
|
||||
{
|
||||
Address addr = Address::CreateNewRandomAddress();
|
||||
Coin coin(addr.getPublicAddress(), 100);
|
||||
|
||||
std::vector<bool> commitment(ZC_CM_SIZE * 8);
|
||||
convertBytesVectorToVector(coin.getCoinCommitment().getCommitmentValue(), commitment);
|
||||
|
||||
std::vector<bool> index;
|
||||
tree.insertElement(commitment, index);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(anchors_flush_test)
|
||||
{
|
||||
CCoinsViewTest base;
|
||||
uint256 newrt;
|
||||
{
|
||||
CCoinsViewCacheTest cache(&base);
|
||||
IncrementalMerkleTree tree(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), tree));
|
||||
appendRandomCommitment(tree);
|
||||
|
||||
{
|
||||
std::vector<unsigned char> newrt_v(32);
|
||||
tree.getRootValue(newrt_v);
|
||||
newrt = uint256(newrt_v);
|
||||
}
|
||||
|
||||
cache.PushAnchor(tree);
|
||||
cache.Flush();
|
||||
}
|
||||
|
||||
{
|
||||
CCoinsViewCacheTest cache(&base);
|
||||
IncrementalMerkleTree tree(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), tree));
|
||||
|
||||
// Get the cached entry.
|
||||
BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), tree));
|
||||
|
||||
uint256 check_rt;
|
||||
{
|
||||
std::vector<unsigned char> newrt_v(32);
|
||||
tree.getRootValue(newrt_v);
|
||||
check_rt = uint256(newrt_v);
|
||||
}
|
||||
|
||||
BOOST_CHECK(check_rt == newrt);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(anchors_test)
|
||||
{
|
||||
// TODO: These tests should be more methodical.
|
||||
// Or, integrate with Bitcoin's tests later.
|
||||
|
||||
CCoinsViewTest base;
|
||||
CCoinsViewCacheTest cache(&base);
|
||||
|
||||
BOOST_CHECK(cache.GetBestAnchor() == uint256());
|
||||
|
||||
{
|
||||
IncrementalMerkleTree tree(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
|
||||
BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), tree));
|
||||
appendRandomCommitment(tree);
|
||||
appendRandomCommitment(tree);
|
||||
appendRandomCommitment(tree);
|
||||
appendRandomCommitment(tree);
|
||||
appendRandomCommitment(tree);
|
||||
appendRandomCommitment(tree);
|
||||
appendRandomCommitment(tree);
|
||||
tree.prune();
|
||||
|
||||
IncrementalMerkleTree save_tree_for_later(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
save_tree_for_later.setTo(tree);
|
||||
|
||||
uint256 newrt;
|
||||
uint256 newrt2;
|
||||
{
|
||||
std::vector<unsigned char> newrt_v(32);
|
||||
tree.getRootValue(newrt_v);
|
||||
|
||||
newrt = uint256(newrt_v);
|
||||
}
|
||||
|
||||
cache.PushAnchor(tree);
|
||||
BOOST_CHECK(cache.GetBestAnchor() == newrt);
|
||||
|
||||
{
|
||||
IncrementalMerkleTree confirm_same(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), confirm_same));
|
||||
|
||||
uint256 confirm_rt;
|
||||
{
|
||||
std::vector<unsigned char> newrt_v(32);
|
||||
confirm_same.getRootValue(newrt_v);
|
||||
|
||||
confirm_rt = uint256(newrt_v);
|
||||
}
|
||||
|
||||
BOOST_CHECK(confirm_rt == newrt);
|
||||
}
|
||||
|
||||
appendRandomCommitment(tree);
|
||||
appendRandomCommitment(tree);
|
||||
tree.prune();
|
||||
|
||||
{
|
||||
std::vector<unsigned char> newrt_v(32);
|
||||
tree.getRootValue(newrt_v);
|
||||
|
||||
newrt2 = uint256(newrt_v);
|
||||
}
|
||||
|
||||
cache.PushAnchor(tree);
|
||||
BOOST_CHECK(cache.GetBestAnchor() == newrt2);
|
||||
|
||||
IncrementalMerkleTree test_tree(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
BOOST_CHECK(cache.GetAnchorAt(cache.GetBestAnchor(), test_tree));
|
||||
|
||||
{
|
||||
std::vector<unsigned char> a(32);
|
||||
std::vector<unsigned char> b(32);
|
||||
tree.getRootValue(a);
|
||||
test_tree.getRootValue(b);
|
||||
|
||||
BOOST_CHECK(a == b);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<unsigned char> a(32);
|
||||
std::vector<unsigned char> b(32);
|
||||
IncrementalMerkleTree test_tree2(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
cache.GetAnchorAt(newrt, test_tree2);
|
||||
|
||||
uint256 recovered_rt;
|
||||
{
|
||||
std::vector<unsigned char> newrt_v(32);
|
||||
test_tree2.getRootValue(newrt_v);
|
||||
|
||||
recovered_rt = uint256(newrt_v);
|
||||
}
|
||||
|
||||
BOOST_CHECK(recovered_rt == newrt);
|
||||
}
|
||||
|
||||
{
|
||||
cache.PopAnchor(newrt);
|
||||
IncrementalMerkleTree obtain_tree(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
assert(!cache.GetAnchorAt(newrt2, obtain_tree)); // should have been popped off
|
||||
assert(cache.GetAnchorAt(newrt, obtain_tree));
|
||||
|
||||
uint256 recovered_rt;
|
||||
{
|
||||
std::vector<unsigned char> newrt_v(32);
|
||||
obtain_tree.getRootValue(newrt_v);
|
||||
|
||||
recovered_rt = uint256(newrt_v);
|
||||
}
|
||||
|
||||
assert(recovered_rt == newrt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
|
||||
|
||||
static const unsigned int NUM_SIMULATION_ITERATIONS = 40000;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -6,6 +6,7 @@
|
|||
#include "streams.h"
|
||||
#include "hash.h"
|
||||
#include "test/test_bitcoin.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -15,6 +16,39 @@ using namespace std;
|
|||
|
||||
BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(boost_arrays)
|
||||
{
|
||||
boost::array<std::string, 2> test_case = {string("zub"), string("baz")};
|
||||
CDataStream ss(SER_DISK, 0);
|
||||
ss << test_case;
|
||||
|
||||
auto hash = Hash(ss.begin(), ss.end());
|
||||
|
||||
BOOST_CHECK_MESSAGE("037a75620362617a" == HexStr(ss.begin(), ss.end()), HexStr(ss.begin(), ss.end()));
|
||||
BOOST_CHECK_MESSAGE(hash == uint256S("13cb12b2dd098dced0064fe4897c97f907ba3ed36ae470c2e7fc2b1111eba35a"), "actually got: " << hash.ToString());
|
||||
|
||||
{
|
||||
// note: boost array of size 2 should serialize to be the same as a tuple
|
||||
std::pair<std::string, std::string> test_case_2 = {string("zub"), string("baz")};
|
||||
|
||||
CDataStream ss2(SER_DISK, 0);
|
||||
ss2 << test_case_2;
|
||||
|
||||
auto hash2 = Hash(ss2.begin(), ss2.end());
|
||||
|
||||
BOOST_CHECK(hash == hash2);
|
||||
}
|
||||
|
||||
boost::array<std::string, 2> decoded_test_case;
|
||||
ss >> decoded_test_case;
|
||||
|
||||
BOOST_CHECK(decoded_test_case == test_case);
|
||||
|
||||
boost::array<int32_t, 2> test = {100, 200};
|
||||
|
||||
BOOST_CHECK_EQUAL(GetSerializeSize(test, 0, 0), 8);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sizes)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(char(0), 0));
|
||||
|
|
|
@ -101,6 +101,7 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
|||
tx.nLockTime = (insecure_rand() % 2) ? insecure_rand() : 0;
|
||||
int ins = (insecure_rand() % 4) + 1;
|
||||
int outs = fSingle ? ins : (insecure_rand() % 4) + 1;
|
||||
int pours = (insecure_rand() % 4);
|
||||
for (int in = 0; in < ins; in++) {
|
||||
tx.vin.push_back(CTxIn());
|
||||
CTxIn &txin = tx.vin.back();
|
||||
|
@ -115,6 +116,32 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
|||
txout.nValue = insecure_rand() % 100000000;
|
||||
RandomScript(txout.scriptPubKey);
|
||||
}
|
||||
if (tx.nVersion >= 2) {
|
||||
for (int pour = 0; pour < pours; pour++) {
|
||||
CPourTx pourtx;
|
||||
pourtx.vpub_old = insecure_rand() % 100000000;
|
||||
pourtx.vpub_new = insecure_rand() % 100000000;
|
||||
RandomScript(pourtx.scriptPubKey);
|
||||
RandomScript(pourtx.scriptSig);
|
||||
pourtx.anchor = GetRandHash();
|
||||
pourtx.serials[0] = GetRandHash();
|
||||
pourtx.serials[1] = GetRandHash();
|
||||
pourtx.ciphertexts[0] = {insecure_rand() % 100, insecure_rand() % 100};
|
||||
pourtx.ciphertexts[1] = {insecure_rand() % 100, insecure_rand() % 100};
|
||||
pourtx.macs[0] = GetRandHash();
|
||||
pourtx.macs[1] = GetRandHash();
|
||||
{
|
||||
std::vector<unsigned char> txt;
|
||||
int prooflen = insecure_rand() % 1000;
|
||||
for (int i = 0; i < prooflen; i++) {
|
||||
txt.push_back(insecure_rand() % 256);
|
||||
}
|
||||
pourtx.proof = std::string(txt.begin(), txt.end());
|
||||
}
|
||||
|
||||
tx.vpour.push_back(pourtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(sighash_tests, BasicTestingSetup)
|
||||
|
@ -200,6 +227,7 @@ BOOST_AUTO_TEST_CASE(sighash_from_data)
|
|||
stream >> tx;
|
||||
|
||||
CValidationState state;
|
||||
state.SetPerformPourVerification(false); // don't verify the snark
|
||||
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
|
||||
BOOST_CHECK(state.IsValid());
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
CClientUIInterface uiInterface; // Declared but not defined in ui_interface.h
|
||||
CWallet* pwalletMain;
|
||||
libzerocash::ZerocashParams *pzerocashParams;
|
||||
|
||||
extern bool fPrintToConsole;
|
||||
extern void noui_connect();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "main.h"
|
||||
#include "script/script.h"
|
||||
#include "script/script_error.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
@ -24,8 +25,16 @@
|
|||
#include <boost/assign/list_of.hpp>
|
||||
#include "json/json_spirit_writer_template.h"
|
||||
|
||||
#include "libzerocash/ZerocashParams.h"
|
||||
#include "libzerocash/IncrementalMerkleTree.h"
|
||||
#include "libzerocash/PourInput.h"
|
||||
#include "libzerocash/PourOutput.h"
|
||||
#include "libzerocash/Address.h"
|
||||
#include "libzerocash/Coin.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace json_spirit;
|
||||
using namespace libzerocash;
|
||||
|
||||
// In script_tests.cpp
|
||||
extern Array read_json(const std::string& jsondata);
|
||||
|
@ -285,6 +294,209 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
|
|||
return dummyTransactions;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
|
||||
{
|
||||
// We only check that pours are constructed properly
|
||||
// and verify properly here. libsnark tends to segfault
|
||||
// when our snarks or what-have-you are invalid, so
|
||||
// we can't really catch everything here.
|
||||
//
|
||||
// See #471, #520, #459 and probably others.
|
||||
//
|
||||
// There may be ways to use boost tests to catch failing
|
||||
// threads or processes (?) but they appear to not work
|
||||
// on all platforms and would gently push us down an ugly
|
||||
// path. We should just fix the assertions.
|
||||
//
|
||||
// Also, it's generally libzerocash's job to ensure
|
||||
// the integrity of the scheme through its own tests.
|
||||
|
||||
static const unsigned int TEST_TREE_DEPTH = 3;
|
||||
|
||||
// construct the r1cs keypair
|
||||
auto keypair = ZerocashParams::GenerateNewKeyPair(TEST_TREE_DEPTH);
|
||||
ZerocashParams p(
|
||||
TEST_TREE_DEPTH,
|
||||
&keypair
|
||||
);
|
||||
|
||||
// construct a merkle tree
|
||||
IncrementalMerkleTree merkleTree(TEST_TREE_DEPTH);
|
||||
Address addr = Address::CreateNewRandomAddress();
|
||||
Coin coin(addr.getPublicAddress(), 100);
|
||||
|
||||
// commitment from coin
|
||||
std::vector<bool> commitment(ZC_CM_SIZE * 8);
|
||||
convertBytesVectorToVector(coin.getCoinCommitment().getCommitmentValue(), commitment);
|
||||
|
||||
// insert commitment into the merkle tree
|
||||
std::vector<bool> index;
|
||||
merkleTree.insertElement(commitment, index);
|
||||
|
||||
// compute the merkle root we will be working with
|
||||
vector<unsigned char> rt(ZC_ROOT_SIZE);
|
||||
{
|
||||
vector<bool> root_bv(ZC_ROOT_SIZE * 8);
|
||||
merkleTree.getRootValue(root_bv);
|
||||
convertVectorToBytesVector(root_bv, rt);
|
||||
}
|
||||
|
||||
merkle_authentication_path path(TEST_TREE_DEPTH);
|
||||
merkleTree.getWitness(index, path);
|
||||
|
||||
// create CPourTx
|
||||
CScript scriptPubKey;
|
||||
boost::array<PourInput, NUM_POUR_INPUTS> inputs = {
|
||||
PourInput(coin, addr, convertVectorToInt(index), path),
|
||||
PourInput(TEST_TREE_DEPTH) // dummy input of zero value
|
||||
};
|
||||
boost::array<PourOutput, NUM_POUR_OUTPUTS> outputs = {
|
||||
PourOutput(50),
|
||||
PourOutput(50)
|
||||
};
|
||||
|
||||
{
|
||||
CPourTx pourtx(p, scriptPubKey, uint256(rt), inputs, outputs, 0, 0);
|
||||
BOOST_CHECK(pourtx.Verify(p));
|
||||
|
||||
CDataStream ss(SER_DISK, CLIENT_VERSION);
|
||||
ss << pourtx;
|
||||
|
||||
CPourTx pourtx_deserialized;
|
||||
ss >> pourtx_deserialized;
|
||||
|
||||
BOOST_CHECK(pourtx_deserialized == pourtx);
|
||||
BOOST_CHECK(pourtx_deserialized.Verify(p));
|
||||
}
|
||||
|
||||
{
|
||||
// Ensure that the balance equation is working.
|
||||
BOOST_CHECK_THROW(CPourTx(p, scriptPubKey, uint256(rt), inputs, outputs, 10, 0), std::invalid_argument);
|
||||
BOOST_CHECK_THROW(CPourTx(p, scriptPubKey, uint256(rt), inputs, outputs, 0, 10), std::invalid_argument);
|
||||
}
|
||||
|
||||
{
|
||||
// Ensure that it won't verify if the root is changed.
|
||||
auto test = CPourTx(p, scriptPubKey, uint256(rt), inputs, outputs, 0, 0);
|
||||
test.anchor = GetRandHash();
|
||||
BOOST_CHECK(!test.Verify(p));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
||||
{
|
||||
CMutableTransaction tx;
|
||||
tx.nVersion = 2;
|
||||
{
|
||||
// Ensure that empty vin/vout remain invalid without
|
||||
// pours.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
state.SetPerformPourVerification(false); // don't verify the snark
|
||||
|
||||
// No pours, vin and vout, means it should be invalid.
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vin-empty");
|
||||
|
||||
newTx.vin.push_back(CTxIn(uint256S("0000000000000000000000000000000000000000000000000000000000000001"), 0));
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vout-empty");
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
CPourTx *pourtx = &newTx.vpour[0];
|
||||
|
||||
pourtx->serials[0] = GetRandHash();
|
||||
pourtx->serials[1] = GetRandHash();
|
||||
|
||||
BOOST_CHECK_MESSAGE(CheckTransaction(newTx, state), state.GetRejectReason());
|
||||
}
|
||||
{
|
||||
// Ensure that values within the pour are well-formed.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
|
||||
CPourTx *pourtx = &newTx.vpour[0];
|
||||
pourtx->vpub_old = -1;
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_old-negative");
|
||||
|
||||
pourtx->vpub_old = MAX_MONEY + 1;
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_old-toolarge");
|
||||
|
||||
pourtx->vpub_old = 0;
|
||||
pourtx->vpub_new = -1;
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_new-negative");
|
||||
|
||||
pourtx->vpub_new = MAX_MONEY + 1;
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_new-toolarge");
|
||||
|
||||
pourtx->vpub_new = (MAX_MONEY / 2) + 10;
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
|
||||
CPourTx *pourtx2 = &newTx.vpour[1];
|
||||
pourtx2->vpub_new = (MAX_MONEY / 2) + 10;
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-txouttotal-toolarge");
|
||||
}
|
||||
{
|
||||
// Ensure that serials are never duplicated within a transaction.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
CPourTx *pourtx = &newTx.vpour[0];
|
||||
|
||||
pourtx->serials[0] = GetRandHash();
|
||||
pourtx->serials[1] = pourtx->serials[0];
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-pours-serials-duplicate");
|
||||
|
||||
pourtx->serials[1] = GetRandHash();
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
CPourTx *pourtx2 = &newTx.vpour[1];
|
||||
|
||||
pourtx2->serials[0] = GetRandHash();
|
||||
pourtx2->serials[1] = pourtx->serials[0];
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-pours-serials-duplicate");
|
||||
}
|
||||
{
|
||||
// Ensure that coinbase transactions do not have pours.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
CPourTx *pourtx = &newTx.vpour[0];
|
||||
pourtx->serials[0] = GetRandHash();
|
||||
pourtx->serials[1] = GetRandHash();
|
||||
|
||||
newTx.vin.push_back(CTxIn(uint256(), -1));
|
||||
|
||||
{
|
||||
CTransaction finalNewTx(newTx);
|
||||
BOOST_CHECK(finalNewTx.IsCoinBase());
|
||||
}
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-pours");
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_Get)
|
||||
{
|
||||
CBasicKeyStore keystore;
|
||||
|
|
88
src/txdb.cpp
88
src/txdb.cpp
|
@ -17,17 +17,39 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
static const char DB_ANCHOR = 'A';
|
||||
static const char DB_SERIAL = 's';
|
||||
static const char DB_COINS = 'c';
|
||||
static const char DB_BLOCK_FILES = 'f';
|
||||
static const char DB_TXINDEX = 't';
|
||||
static const char DB_BLOCK_INDEX = 'b';
|
||||
|
||||
static const char DB_BEST_BLOCK = 'B';
|
||||
static const char DB_BEST_ANCHOR = 'a';
|
||||
static const char DB_FLAG = 'F';
|
||||
static const char DB_REINDEX_FLAG = 'R';
|
||||
static const char DB_LAST_BLOCK = 'l';
|
||||
|
||||
|
||||
void static BatchWriteAnchor(CLevelDBBatch &batch,
|
||||
const uint256 &croot,
|
||||
const libzerocash::IncrementalMerkleTree &tree,
|
||||
const bool &entered)
|
||||
{
|
||||
if (!entered)
|
||||
batch.Erase(make_pair(DB_ANCHOR, croot));
|
||||
else {
|
||||
batch.Write(make_pair(DB_ANCHOR, croot), tree.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
void static BatchWriteSerial(CLevelDBBatch &batch, const uint256 &serial, const bool &entered) {
|
||||
if (!entered)
|
||||
batch.Erase(make_pair(DB_SERIAL, serial));
|
||||
else
|
||||
batch.Write(make_pair(DB_SERIAL, serial), true);
|
||||
}
|
||||
|
||||
void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) {
|
||||
if (coins.IsPruned())
|
||||
batch.Erase(make_pair(DB_COINS, hash));
|
||||
|
@ -39,9 +61,41 @@ void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) {
|
|||
batch.Write(DB_BEST_BLOCK, hash);
|
||||
}
|
||||
|
||||
void static BatchWriteHashBestAnchor(CLevelDBBatch &batch, const uint256 &hash) {
|
||||
batch.Write(DB_BEST_ANCHOR, hash);
|
||||
}
|
||||
|
||||
CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) {
|
||||
}
|
||||
|
||||
|
||||
bool CCoinsViewDB::GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const {
|
||||
if (rt.IsNull()) {
|
||||
IncrementalMerkleTree new_tree(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
tree.setTo(new_tree);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> tree_serialized;
|
||||
|
||||
bool read = db.Read(make_pair(DB_ANCHOR, rt), tree_serialized);
|
||||
|
||||
if (!read) return read;
|
||||
|
||||
auto tree_deserialized = IncrementalMerkleTreeCompact::deserialize(tree_serialized);
|
||||
|
||||
tree.fromCompactRepresentation(tree_deserialized);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewDB::GetSerial(const uint256 &serial) const {
|
||||
bool spent = false;
|
||||
bool read = db.Read(make_pair(DB_SERIAL, serial), spent);
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const {
|
||||
return db.Read(make_pair(DB_COINS, txid), coins);
|
||||
}
|
||||
|
@ -57,7 +111,18 @@ uint256 CCoinsViewDB::GetBestBlock() const {
|
|||
return hashBestChain;
|
||||
}
|
||||
|
||||
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
|
||||
uint256 CCoinsViewDB::GetBestAnchor() const {
|
||||
uint256 hashBestAnchor;
|
||||
if (!db.Read(DB_BEST_ANCHOR, hashBestAnchor))
|
||||
return uint256();
|
||||
return hashBestAnchor;
|
||||
}
|
||||
|
||||
bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials) {
|
||||
CLevelDBBatch batch;
|
||||
size_t count = 0;
|
||||
size_t changed = 0;
|
||||
|
@ -70,8 +135,29 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
|
|||
CCoinsMap::iterator itOld = it++;
|
||||
mapCoins.erase(itOld);
|
||||
}
|
||||
|
||||
for (CAnchorsMap::iterator it = mapAnchors.begin(); it != mapAnchors.end();) {
|
||||
if (it->second.flags & CAnchorsCacheEntry::DIRTY) {
|
||||
BatchWriteAnchor(batch, it->first, it->second.tree, it->second.entered);
|
||||
// TODO: changed++?
|
||||
}
|
||||
CAnchorsMap::iterator itOld = it++;
|
||||
mapAnchors.erase(itOld);
|
||||
}
|
||||
|
||||
for (CSerialsMap::iterator it = mapSerials.begin(); it != mapSerials.end();) {
|
||||
if (it->second.flags & CSerialsCacheEntry::DIRTY) {
|
||||
BatchWriteSerial(batch, it->first, it->second.entered);
|
||||
// TODO: changed++?
|
||||
}
|
||||
CSerialsMap::iterator itOld = it++;
|
||||
mapSerials.erase(itOld);
|
||||
}
|
||||
|
||||
if (!hashBlock.IsNull())
|
||||
BatchWriteHashBestChain(batch, hashBlock);
|
||||
if (!hashAnchor.IsNull())
|
||||
BatchWriteHashBestAnchor(batch, hashAnchor);
|
||||
|
||||
LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
|
||||
return db.WriteBatch(batch);
|
||||
|
|
11
src/txdb.h
11
src/txdb.h
|
@ -14,6 +14,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "libzerocash/IncrementalMerkleTree.h"
|
||||
|
||||
class CBlockFileInfo;
|
||||
class CBlockIndex;
|
||||
struct CDiskTxPos;
|
||||
|
@ -34,10 +36,17 @@ protected:
|
|||
public:
|
||||
CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||
|
||||
bool GetAnchorAt(const uint256 &rt, libzerocash::IncrementalMerkleTree &tree) const;
|
||||
bool GetSerial(const uint256 &serial) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
|
||||
uint256 GetBestAnchor() const;
|
||||
bool BatchWrite(CCoinsMap &mapCoins,
|
||||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials);
|
||||
bool GetStats(CCoinsStats &stats) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -99,6 +99,11 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
|||
const CTransaction& tx = mapTx[hash].GetTx();
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i);
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
BOOST_FOREACH(const uint256 &serial, pour.serials) {
|
||||
mapSerials[serial] = &tx;
|
||||
}
|
||||
}
|
||||
nTransactionsUpdated++;
|
||||
totalTxSize += entry.GetTxSize();
|
||||
minerPolicyEstimator->processTransaction(entry, fCurrentEstimate);
|
||||
|
@ -143,6 +148,11 @@ void CTxMemPool::remove(const CTransaction &origTx, std::list<CTransaction>& rem
|
|||
}
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
mapNextTx.erase(txin.prevout);
|
||||
BOOST_FOREACH(const CPourTx& pour, tx.vpour) {
|
||||
BOOST_FOREACH(const uint256& serial, pour.serials) {
|
||||
mapSerials.erase(serial);
|
||||
}
|
||||
}
|
||||
|
||||
removed.push_back(tx);
|
||||
totalTxSize -= mapTx[hash].GetTxSize();
|
||||
|
@ -178,6 +188,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<CTransaction> transactionsToRemove;
|
||||
|
||||
for (std::map<uint256, CTxMemPoolEntry>::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<CTransaction> removed;
|
||||
remove(tx, removed, true);
|
||||
}
|
||||
}
|
||||
|
||||
void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed)
|
||||
{
|
||||
// Remove transactions which depend on inputs of tx, recursively
|
||||
|
@ -193,6 +229,19 @@ void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
BOOST_FOREACH(const uint256 &serial, pour.serials) {
|
||||
std::map<uint256, const CTransaction*>::iterator it = mapSerials.find(serial);
|
||||
if (it != mapSerials.end()) {
|
||||
const CTransaction &txConflict = *it->second;
|
||||
if (txConflict != tx)
|
||||
{
|
||||
remove(txConflict, removed, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -265,6 +314,15 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||
assert(it3->second.n == i);
|
||||
i++;
|
||||
}
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
BOOST_FOREACH(const uint256 &serial, pour.serials) {
|
||||
assert(!pcoins->GetSerial(serial));
|
||||
}
|
||||
|
||||
// TODO: chained pours
|
||||
libzerocash::IncrementalMerkleTree tree(INCREMENTAL_MERKLE_TREE_DEPTH);
|
||||
assert(pcoins->GetAnchorAt(pour.anchor, tree));
|
||||
}
|
||||
if (fDependsWait)
|
||||
waitingOnDependants.push_back(&it->second);
|
||||
else {
|
||||
|
@ -298,6 +356,14 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||
assert(it->first == it->second.ptx->vin[it->second.n].prevout);
|
||||
}
|
||||
|
||||
for (std::map<uint256, const CTransaction*>::const_iterator it = mapSerials.begin(); it != mapSerials.end(); it++) {
|
||||
uint256 hash = it->second->GetHash();
|
||||
map<uint256, CTxMemPoolEntry>::const_iterator it2 = mapTx.find(hash);
|
||||
const CTransaction& tx = it2->second.GetTx();
|
||||
assert(it2 != mapTx.end());
|
||||
assert(&tx == it->second);
|
||||
}
|
||||
|
||||
assert(totalTxSize == checkTotal);
|
||||
}
|
||||
|
||||
|
@ -404,6 +470,13 @@ bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const
|
|||
|
||||
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
|
||||
|
||||
bool CCoinsViewMemPool::GetSerial(const uint256 &serial) const {
|
||||
if (mempool.mapSerials.count(serial))
|
||||
return true;
|
||||
|
||||
return base->GetSerial(serial);
|
||||
}
|
||||
|
||||
bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const {
|
||||
// If an entry in the mempool exists, always return that one, as it's guaranteed to never
|
||||
// conflict with the underlying cache, and it cannot have pruned entries (as it contains full)
|
||||
|
|
|
@ -98,6 +98,7 @@ public:
|
|||
mutable CCriticalSection cs;
|
||||
std::map<uint256, CTxMemPoolEntry> mapTx;
|
||||
std::map<COutPoint, CInPoint> mapNextTx;
|
||||
std::map<uint256, const CTransaction*> mapSerials;
|
||||
std::map<uint256, std::pair<double, CAmount> > mapDeltas;
|
||||
|
||||
CTxMemPool(const CFeeRate& _minRelayFee);
|
||||
|
@ -114,6 +115,7 @@ public:
|
|||
|
||||
bool addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, bool fCurrentEstimate = true);
|
||||
void remove(const CTransaction &tx, std::list<CTransaction>& removed, bool fRecursive = false);
|
||||
void removeWithAnchor(const uint256 &invalidRoot);
|
||||
void removeCoinbaseSpends(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight);
|
||||
void removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed);
|
||||
void removeForBlock(const std::vector<CTransaction>& vtx, unsigned int nBlockHeight,
|
||||
|
@ -175,6 +177,7 @@ protected:
|
|||
|
||||
public:
|
||||
CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn);
|
||||
bool GetSerial(const uint256 &txid) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
};
|
||||
|
|
|
@ -73,12 +73,14 @@ class CBlockUndo
|
|||
{
|
||||
public:
|
||||
std::vector<CTxUndo> vtxundo; // for all but the coinbase
|
||||
uint256 old_tree_root;
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
||||
READWRITE(vtxundo);
|
||||
READWRITE(old_tree_root);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
51
src/util.cpp
51
src/util.cpp
|
@ -430,8 +430,59 @@ boost::filesystem::path GetDefaultDataDir()
|
|||
|
||||
static boost::filesystem::path pathCached;
|
||||
static boost::filesystem::path pathCachedNetSpecific;
|
||||
static boost::filesystem::path zc_paramsPathCached;
|
||||
static CCriticalSection csPathCached;
|
||||
|
||||
static boost::filesystem::path ZC_GetBaseParamsDir()
|
||||
{
|
||||
// Copied from GetDefaultDataDir and adapter for zcash params.
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
// Windows < Vista: C:\Documents and Settings\Username\Application Data\ZcashParams
|
||||
// Windows >= Vista: C:\Users\Username\AppData\Roaming\ZcashParams
|
||||
// Mac: ~/Library/Application Support/ZcashParams
|
||||
// Unix: ~/.zcash-params
|
||||
#ifdef WIN32
|
||||
// Windows
|
||||
return GetSpecialFolderPath(CSIDL_APPDATA) / "ZcashParams";
|
||||
#else
|
||||
fs::path pathRet;
|
||||
char* pszHome = getenv("HOME");
|
||||
if (pszHome == NULL || strlen(pszHome) == 0)
|
||||
pathRet = fs::path("/");
|
||||
else
|
||||
pathRet = fs::path(pszHome);
|
||||
#ifdef MAC_OSX
|
||||
// Mac
|
||||
pathRet /= "Library/Application Support";
|
||||
TryCreateDirectory(pathRet);
|
||||
return pathRet / "ZcashParams";
|
||||
#else
|
||||
// Unix
|
||||
return pathRet / ".zcash-params";
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
const boost::filesystem::path &ZC_GetParamsDir()
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
LOCK(csPathCached); // Reuse the same lock as upstream.
|
||||
|
||||
fs::path &path = zc_paramsPathCached;
|
||||
|
||||
// This can be called during exceptions by LogPrintf(), so we cache the
|
||||
// value so we don't have to do memory allocations after that.
|
||||
if (!path.empty())
|
||||
return path;
|
||||
|
||||
path = ZC_GetBaseParamsDir();
|
||||
path /= BaseParams().DataDir();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
const boost::filesystem::path &GetDataDir(bool fNetSpecific)
|
||||
{
|
||||
namespace fs = boost::filesystem;
|
||||
|
|
|
@ -104,6 +104,8 @@ static inline bool error(const char* format)
|
|||
return false;
|
||||
}
|
||||
|
||||
const boost::filesystem::path &ZC_GetParamsDir();
|
||||
|
||||
void PrintExceptionContinue(const std::exception *pex, const char* pszThread);
|
||||
void ParseParameters(int argc, const char*const argv[]);
|
||||
void FileCommit(FILE *fileout);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "utilmoneystr.h"
|
||||
#include "wallet.h"
|
||||
#include "walletdb.h"
|
||||
#include "primitives/transaction.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -2343,3 +2344,275 @@ Value listunspent(const Array& params, bool fHelp)
|
|||
|
||||
return results;
|
||||
}
|
||||
|
||||
Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp)) {
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
if (fHelp || params.size() != 2) {
|
||||
throw runtime_error(
|
||||
"zcrawreceive zcsecretkey encryptedbucket\n"
|
||||
"\n"
|
||||
"Decrypts encryptedbucket and checks if the coin commitments\n"
|
||||
"are in the blockchain as indicated by the \"exists\" result.\n"
|
||||
"\n"
|
||||
"Output: {\n"
|
||||
" \"amount\": value,\n"
|
||||
" \"bucket\": cleartextbucket,\n"
|
||||
" \"exists\": exists\n"
|
||||
"}\n"
|
||||
);
|
||||
}
|
||||
|
||||
RPCTypeCheck(params, boost::assign::list_of(str_type)(str_type));
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
std::vector<unsigned char> 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<unsigned char> 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)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp)) {
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
if (fHelp || params.size() != 5) {
|
||||
throw runtime_error(
|
||||
"zcrawpour rawtx inputs outputs vpub_old vpub_new\n"
|
||||
" inputs: a JSON object mapping {bucket: zcsecretkey, ...}\n"
|
||||
" outputs: a JSON object mapping {zcaddr: value, ...}\n"
|
||||
"\n"
|
||||
"Splices a Pour into rawtx. Inputs are unilaterally confidential.\n"
|
||||
"Outputs are confidential between sender/receiver. The vpub_old and\n"
|
||||
"vpub_new values are globally public and move transparent value into\n"
|
||||
"or out of the confidential value store, respectively.\n"
|
||||
"\n"
|
||||
"Note: The caller is responsible for delivering the output enc1 and\n"
|
||||
"enc2 to the appropriate recipients, as well as signing rawtxout and\n"
|
||||
"ensuring it is mined. (A future RPC call will deliver the confidential\n"
|
||||
"payments in-band on the blockchain.)\n"
|
||||
"\n"
|
||||
"Output: {\n"
|
||||
" \"encryptedbucket1\": enc1,\n"
|
||||
" \"encryptedbucket2\": enc2,\n"
|
||||
" \"rawtxn\": rawtxout\n"
|
||||
"}\n"
|
||||
);
|
||||
}
|
||||
|
||||
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()))
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
|
||||
Object inputs = params[1].get_obj();
|
||||
Object outputs = params[2].get_obj();
|
||||
|
||||
CAmount vpub_old(0);
|
||||
CAmount vpub_new(0);
|
||||
|
||||
if (params[3].get_real() != 0.0)
|
||||
vpub_old = AmountFromValue(params[3]);
|
||||
|
||||
if (params[4].get_real() != 0.0)
|
||||
vpub_new = AmountFromValue(params[4]);
|
||||
|
||||
std::vector<PourInput> vpourin;
|
||||
std::vector<PourOutput> vpourout;
|
||||
|
||||
uint256 anchor;
|
||||
|
||||
BOOST_FOREACH(const Pair& s, inputs)
|
||||
{
|
||||
CDataStream ssData(ParseHexV(s.name_, "bucket"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
uint64_t value;
|
||||
std::vector<unsigned char> rho;
|
||||
std::vector<unsigned char> r;
|
||||
|
||||
ssData >> value;
|
||||
ssData >> rho;
|
||||
ssData >> r;
|
||||
|
||||
std::vector<unsigned char> 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<unsigned char> 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));
|
||||
}
|
||||
|
||||
while (vpourin.size() < NUM_POUR_INPUTS) {
|
||||
vpourin.push_back(PourInput(INCREMENTAL_MERKLE_TREE_DEPTH));
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const Pair& s, outputs)
|
||||
{
|
||||
libzerocash::PublicAddress addrTo;
|
||||
|
||||
{
|
||||
CDataStream ssData(ParseHexV(s.name_, "to_address"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
std::vector<unsigned char> pubAddressSecret;
|
||||
std::string encryptionPublicKey;
|
||||
|
||||
ssData >> pubAddressSecret;
|
||||
ssData >> encryptionPublicKey;
|
||||
|
||||
addrTo = libzerocash::PublicAddress(pubAddressSecret, encryptionPublicKey);
|
||||
}
|
||||
CAmount nAmount = AmountFromValue(s.value_);
|
||||
|
||||
libzerocash::Coin coin(addrTo, nAmount);
|
||||
libzerocash::PourOutput output(coin, addrTo);
|
||||
|
||||
vpourout.push_back(output);
|
||||
}
|
||||
|
||||
while (vpourout.size() < NUM_POUR_OUTPUTS) {
|
||||
vpourout.push_back(PourOutput(0));
|
||||
}
|
||||
|
||||
// TODO
|
||||
if (vpourout.size() != NUM_POUR_INPUTS || vpourin.size() != NUM_POUR_OUTPUTS) {
|
||||
throw runtime_error("unsupported pour input/output counts");
|
||||
}
|
||||
|
||||
CScript scriptPubKey;
|
||||
CPourTx pourtx(*pzerocashParams,
|
||||
scriptPubKey,
|
||||
anchor,
|
||||
{vpourin[0], vpourin[1]},
|
||||
{vpourout[0], vpourout[1]},
|
||||
vpub_old,
|
||||
vpub_new);
|
||||
|
||||
assert(pourtx.Verify(*pzerocashParams));
|
||||
|
||||
CMutableTransaction mtx(tx);
|
||||
mtx.nVersion = 2;
|
||||
mtx.vpour.push_back(pourtx);
|
||||
|
||||
CTransaction rawTx(mtx);
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << rawTx;
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("encryptedbucket1", HexStr(pourtx.ciphertexts[0].begin(), pourtx.ciphertexts[0].end())));
|
||||
result.push_back(Pair("encryptedbucket2", HexStr(pourtx.ciphertexts[1].begin(), pourtx.ciphertexts[1].end())));
|
||||
result.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end())));
|
||||
return result;
|
||||
}
|
||||
|
||||
Value zc_raw_keygen(const json_spirit::Array& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp)) {
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
if (fHelp || params.size() != 0) {
|
||||
throw runtime_error(
|
||||
"zcrawkeygen\n"
|
||||
"\n"
|
||||
"Generate a zcaddr which can send and receive confidential values.\n"
|
||||
"\n"
|
||||
"Output: {\n"
|
||||
" \"zcaddress\": zcaddr,\n"
|
||||
" \"zcsecretkey\": zcsecretkey,\n"
|
||||
"}\n"
|
||||
);
|
||||
}
|
||||
|
||||
auto zckeypair = libzerocash::Address::CreateNewRandomAddress();
|
||||
|
||||
CDataStream pub(SER_NETWORK, PROTOCOL_VERSION);
|
||||
CDataStream priv(SER_NETWORK, PROTOCOL_VERSION);
|
||||
|
||||
pub << zckeypair.getPublicAddress().getPublicAddressSecret(); // a_pk
|
||||
pub << zckeypair.getPublicAddress().getEncryptionPublicKey(); // pk_enc
|
||||
|
||||
priv << zckeypair.getPrivateAddress().getAddressSecret(); // a_sk
|
||||
priv << zckeypair.getPrivateAddress().getEncryptionSecretKey(); // sk_enc
|
||||
|
||||
std::string pub_hex = HexStr(pub.begin(), pub.end());
|
||||
std::string priv_hex = HexStr(priv.begin(), priv.end());
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("zcaddress", pub_hex));
|
||||
result.push_back(Pair("zcsecretkey", priv_hex));
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1051,6 +1051,67 @@ 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<bool> 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<bool> commitment_bv(ZC_CM_SIZE * 8);
|
||||
std::vector<bool> index;
|
||||
std::vector<unsigned char> commitment_value(bucket_commitment.begin(), bucket_commitment.end());
|
||||
libzerocash::convertBytesVectorToVector(commitment_value, commitment_bv);
|
||||
assert(tree.insertElement(commitment_bv, index));
|
||||
|
||||
if (bucket_commitment == commitment) {
|
||||
// We've found it! Now, we construct a witness.
|
||||
res = true;
|
||||
commitment_index = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<unsigned char> 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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -6,10 +6,13 @@ PARAMS_DIR="$HOME/.zcash-params"
|
|||
|
||||
REGTEST_PKEY_NAME='zc-testnet-public-alpha-proving.key'
|
||||
REGTEST_VKEY_NAME='zc-testnet-public-alpha-verification.key'
|
||||
REGTEST_PKEY_URL="https://zca.sh/downloads/$REGTEST_PKEY_NAME"
|
||||
REGTEST_VKEY_URL="https://zca.sh/downloads/$REGTEST_VKEY_NAME"
|
||||
REGTEST_PKEY_URL="https://z.cash/downloads/$REGTEST_PKEY_NAME"
|
||||
REGTEST_VKEY_URL="https://z.cash/downloads/$REGTEST_VKEY_NAME"
|
||||
REGTEST_DIR="$PARAMS_DIR/regtest"
|
||||
|
||||
# This should have the same params as regtest. We use symlinks for now.
|
||||
TESTNET3_DIR="$PARAMS_DIR/testnet3"
|
||||
|
||||
|
||||
# Note: This assumes cwd is set appropriately!
|
||||
function fetch_params {
|
||||
|
@ -61,9 +64,19 @@ cd "$REGTEST_DIR"
|
|||
fetch_params "$REGTEST_PKEY_URL"
|
||||
fetch_params "$REGTEST_VKEY_URL"
|
||||
|
||||
cd ..
|
||||
|
||||
echo 'Updating testnet3 symlinks to regtest parameters.'
|
||||
mkdir -p "$TESTNET3_DIR"
|
||||
ln -sf "../regtest/$REGTEST_PKEY_NAME" "$TESTNET3_DIR/$REGTEST_PKEY_NAME"
|
||||
ln -sf "../regtest/$REGTEST_VKEY_NAME" "$TESTNET3_DIR/$REGTEST_VKEY_NAME"
|
||||
|
||||
# Now verify their hashes:
|
||||
echo 'Verifying parameter file integrity via sha256sum...'
|
||||
sha256sum --check - <<EOF
|
||||
7844a96933979158886a5b69fb163f49de76120fa1dcfc33b16c83c134e61817 $REGTEST_PKEY_NAME
|
||||
6902fd687bface72e572a7cda57f6da5a0c606c7b9769f30becd255e57924f41 $REGTEST_VKEY_NAME
|
||||
7844a96933979158886a5b69fb163f49de76120fa1dcfc33b16c83c134e61817 regtest/$REGTEST_PKEY_NAME
|
||||
7844a96933979158886a5b69fb163f49de76120fa1dcfc33b16c83c134e61817 testnet3/$REGTEST_PKEY_NAME
|
||||
6902fd687bface72e572a7cda57f6da5a0c606c7b9769f30becd255e57924f41 regtest/$REGTEST_VKEY_NAME
|
||||
6902fd687bface72e572a7cda57f6da5a0c606c7b9769f30becd255e57924f41 testnet3/$REGTEST_VKEY_NAME
|
||||
EOF
|
||||
|
||||
|
|
Loading…
Reference in New Issue