Auto merge of #1090 - ebfull:rename-pour-and-serial, r=ebfull
Rename things to match protocol specification This is a pass through the code to eliminate usage of "pour" and "serial" and any other outdated terminology. Closes #602
This commit is contained in:
commit
4d459f93a4
|
@ -27,8 +27,8 @@ testScripts=(
|
|||
'merkle_blocks.py'
|
||||
'signrawtransactions.py'
|
||||
'walletbackup.py'
|
||||
'zcpour.py'
|
||||
'zcpourdoublespend.py'
|
||||
'zcjoinsplit.py'
|
||||
'zcjoinsplitdoublespend.py'
|
||||
);
|
||||
testScriptsExt=(
|
||||
'bipdersig-p2p.py'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python2
|
||||
|
||||
#
|
||||
# Test Pour semantics
|
||||
# Test joinsplit semantics
|
||||
#
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
|
@ -11,7 +11,7 @@ import os
|
|||
import shutil
|
||||
import sys
|
||||
|
||||
class PourTxTest(BitcoinTestFramework):
|
||||
class JoinSplitTest(BitcoinTestFramework):
|
||||
def setup_network(self):
|
||||
self.nodes = []
|
||||
self.is_network_split = False
|
||||
|
@ -24,27 +24,27 @@ class PourTxTest(BitcoinTestFramework):
|
|||
|
||||
(total_in, inputs) = gather_inputs(self.nodes[0], 40)
|
||||
protect_tx = self.nodes[0].createrawtransaction(inputs, {})
|
||||
pour_result = self.nodes[0].zcrawpour(protect_tx, {}, {zcaddress:39.9}, 39.9, 0)
|
||||
joinsplit_result = self.nodes[0].zcrawjoinsplit(protect_tx, {}, {zcaddress:39.9}, 39.9, 0)
|
||||
|
||||
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, pour_result["encryptedbucket1"])
|
||||
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"])
|
||||
assert_equal(receive_result["exists"], False)
|
||||
|
||||
protect_tx = self.nodes[0].signrawtransaction(pour_result["rawtxn"])
|
||||
protect_tx = self.nodes[0].signrawtransaction(joinsplit_result["rawtxn"])
|
||||
self.nodes[0].sendrawtransaction(protect_tx["hex"])
|
||||
self.nodes[0].generate(1)
|
||||
|
||||
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, pour_result["encryptedbucket1"])
|
||||
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"])
|
||||
assert_equal(receive_result["exists"], True)
|
||||
|
||||
pour_tx = self.nodes[0].createrawtransaction([], {})
|
||||
pour_result = self.nodes[0].zcrawpour(pour_tx, {receive_result["bucket"] : zcsecretkey}, {zcaddress: 39.8}, 0, 0.1)
|
||||
joinsplit_tx = self.nodes[0].createrawtransaction([], {})
|
||||
joinsplit_result = self.nodes[0].zcrawjoinsplit(joinsplit_tx, {receive_result["note"] : zcsecretkey}, {zcaddress: 39.8}, 0, 0.1)
|
||||
|
||||
self.nodes[0].sendrawtransaction(pour_result["rawtxn"])
|
||||
self.nodes[0].sendrawtransaction(joinsplit_result["rawtxn"])
|
||||
self.nodes[0].generate(1)
|
||||
|
||||
print "Done!"
|
||||
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, pour_result["encryptedbucket1"])
|
||||
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"])
|
||||
assert_equal(receive_result["exists"], True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
PourTxTest().main()
|
||||
JoinSplitTest().main()
|
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env python2
|
||||
|
||||
#
|
||||
# Tests a Pour double-spend and a subsequent reorg.
|
||||
# Tests a joinsplit double-spend and a subsequent reorg.
|
||||
#
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
|
@ -12,10 +12,10 @@ import shutil
|
|||
import sys
|
||||
import time
|
||||
|
||||
class PourTxTest(BitcoinTestFramework):
|
||||
class JoinSplitTest(BitcoinTestFramework):
|
||||
def setup_network(self):
|
||||
# Start with split network:
|
||||
return super(PourTxTest, self).setup_network(True)
|
||||
return super(JoinSplitTest, self).setup_network(True)
|
||||
|
||||
def txid_in_mempool(self, node, txid):
|
||||
exception_triggered = False
|
||||
|
@ -27,7 +27,7 @@ class PourTxTest(BitcoinTestFramework):
|
|||
|
||||
return not exception_triggered
|
||||
|
||||
def cannot_pour(self, node, txn):
|
||||
def cannot_joinsplit(self, node, txn):
|
||||
exception_triggered = False
|
||||
|
||||
try:
|
||||
|
@ -37,8 +37,8 @@ class PourTxTest(BitcoinTestFramework):
|
|||
|
||||
return exception_triggered
|
||||
|
||||
def expect_cannot_pour(self, node, txn):
|
||||
assert_equal(self.cannot_pour(node, txn), True)
|
||||
def expect_cannot_joinsplit(self, node, txn):
|
||||
assert_equal(self.cannot_joinsplit(node, txn), True)
|
||||
|
||||
def run_test(self):
|
||||
# All nodes should start with 250 BTC:
|
||||
|
@ -56,7 +56,7 @@ class PourTxTest(BitcoinTestFramework):
|
|||
for i in range(4):
|
||||
(total_in, inputs) = gather_inputs(self.nodes[i], 40)
|
||||
pool[i] = self.nodes[i].createrawtransaction(inputs, {})
|
||||
pool[i] = self.nodes[i].zcrawpour(pool[i], {}, {zcaddress:39.9}, 39.9, 0)
|
||||
pool[i] = self.nodes[i].zcrawjoinsplit(pool[i], {}, {zcaddress:39.9}, 39.9, 0)
|
||||
signed = self.nodes[i].signrawtransaction(pool[i]["rawtxn"])
|
||||
|
||||
# send the tx to both halves of the network
|
||||
|
@ -64,54 +64,54 @@ class PourTxTest(BitcoinTestFramework):
|
|||
self.nodes[0].generate(1)
|
||||
self.nodes[2].sendrawtransaction(signed["hex"])
|
||||
self.nodes[2].generate(1)
|
||||
pool[i] = pool[i]["encryptedbucket1"]
|
||||
pool[i] = pool[i]["encryptednote1"]
|
||||
|
||||
sync_blocks(self.nodes[0:2])
|
||||
sync_blocks(self.nodes[2:4])
|
||||
|
||||
# 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)
|
||||
enc_note = pool[i]
|
||||
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, enc_note)
|
||||
assert_equal(receive_result["exists"], True)
|
||||
pool[i] = receive_result["bucket"]
|
||||
pool[i] = receive_result["note"]
|
||||
|
||||
# Extra confirmations
|
||||
receive_result = self.nodes[1].zcrawreceive(zcsecretkey, enc_bucket)
|
||||
receive_result = self.nodes[1].zcrawreceive(zcsecretkey, enc_note)
|
||||
assert_equal(receive_result["exists"], True)
|
||||
|
||||
receive_result = self.nodes[2].zcrawreceive(zcsecretkey, enc_bucket)
|
||||
receive_result = self.nodes[2].zcrawreceive(zcsecretkey, enc_note)
|
||||
assert_equal(receive_result["exists"], True)
|
||||
|
||||
receive_result = self.nodes[3].zcrawreceive(zcsecretkey, enc_bucket)
|
||||
receive_result = self.nodes[3].zcrawreceive(zcsecretkey, enc_note)
|
||||
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:(39.9*2)-0.1},
|
||||
0, 0.1)
|
||||
# Create joinsplit {A, B}->{*}
|
||||
joinsplit_AB = self.nodes[0].zcrawjoinsplit(blank_tx,
|
||||
{pool[0] : zcsecretkey, pool[1] : zcsecretkey},
|
||||
{zcaddress:(39.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:(39.9*2)-0.1},
|
||||
0, 0.1)
|
||||
# Create joinsplit {B, C}->{*}
|
||||
joinsplit_BC = self.nodes[0].zcrawjoinsplit(blank_tx,
|
||||
{pool[1] : zcsecretkey, pool[2] : zcsecretkey},
|
||||
{zcaddress:(39.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:(39.9*2)-0.1},
|
||||
0, 0.1)
|
||||
# Create joinsplit {C, D}->{*}
|
||||
joinsplit_CD = self.nodes[0].zcrawjoinsplit(blank_tx,
|
||||
{pool[2] : zcsecretkey, pool[3] : zcsecretkey},
|
||||
{zcaddress:(39.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:(39.9*2)-0.1},
|
||||
0, 0.1)
|
||||
# Create joinsplit {A, D}->{*}
|
||||
joinsplit_AD = self.nodes[0].zcrawjoinsplit(blank_tx,
|
||||
{pool[0] : zcsecretkey, pool[3] : zcsecretkey},
|
||||
{zcaddress:(39.9*2)-0.1},
|
||||
0, 0.1)
|
||||
|
||||
# (a) Node 0 will spend pour AB, then attempt to
|
||||
# (a) Node 0 will spend joinsplit AB, then attempt to
|
||||
# double-spend it with BC. It should fail before and
|
||||
# after Node 0 mines blocks.
|
||||
#
|
||||
|
@ -126,9 +126,9 @@ class PourTxTest(BitcoinTestFramework):
|
|||
|
||||
# (a)
|
||||
|
||||
AB_txid = self.nodes[0].sendrawtransaction(pour_AB["rawtxn"])
|
||||
AB_txid = self.nodes[0].sendrawtransaction(joinsplit_AB["rawtxn"])
|
||||
|
||||
self.expect_cannot_pour(self.nodes[0], pour_BC["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[0], joinsplit_BC["rawtxn"])
|
||||
|
||||
# Wait until node[1] receives AB before we attempt to double-spend
|
||||
# with BC.
|
||||
|
@ -139,17 +139,17 @@ class PourTxTest(BitcoinTestFramework):
|
|||
time.sleep(0.2)
|
||||
print "Done!\n"
|
||||
|
||||
self.expect_cannot_pour(self.nodes[1], pour_BC["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[1], joinsplit_BC["rawtxn"])
|
||||
|
||||
# Generate a block
|
||||
self.nodes[0].generate(1)
|
||||
sync_blocks(self.nodes[0:2])
|
||||
|
||||
self.expect_cannot_pour(self.nodes[0], pour_BC["rawtxn"])
|
||||
self.expect_cannot_pour(self.nodes[1], pour_BC["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[0], joinsplit_BC["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[1], joinsplit_BC["rawtxn"])
|
||||
|
||||
# (b)
|
||||
self.nodes[2].sendrawtransaction(pour_BC["rawtxn"])
|
||||
self.nodes[2].sendrawtransaction(joinsplit_BC["rawtxn"])
|
||||
self.nodes[2].generate(5)
|
||||
|
||||
# Connect the two nodes
|
||||
|
@ -158,26 +158,26 @@ class PourTxTest(BitcoinTestFramework):
|
|||
sync_blocks(self.nodes)
|
||||
|
||||
# AB and 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_joinsplit(self.nodes[0], joinsplit_AB["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[0], joinsplit_CD["rawtxn"])
|
||||
|
||||
self.expect_cannot_pour(self.nodes[1], pour_AB["rawtxn"])
|
||||
self.expect_cannot_pour(self.nodes[1], pour_CD["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[1], joinsplit_AB["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[1], joinsplit_CD["rawtxn"])
|
||||
|
||||
self.expect_cannot_pour(self.nodes[2], pour_AB["rawtxn"])
|
||||
self.expect_cannot_pour(self.nodes[2], pour_CD["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[2], joinsplit_AB["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[2], joinsplit_CD["rawtxn"])
|
||||
|
||||
self.expect_cannot_pour(self.nodes[3], pour_AB["rawtxn"])
|
||||
self.expect_cannot_pour(self.nodes[3], pour_CD["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[3], joinsplit_AB["rawtxn"])
|
||||
self.expect_cannot_joinsplit(self.nodes[3], joinsplit_CD["rawtxn"])
|
||||
|
||||
# (c)
|
||||
# AD should be possible to send due to the reorg that
|
||||
# tossed out AB.
|
||||
|
||||
self.nodes[0].sendrawtransaction(pour_AD["rawtxn"])
|
||||
self.nodes[0].sendrawtransaction(joinsplit_AD["rawtxn"])
|
||||
self.nodes[0].generate(1)
|
||||
|
||||
sync_blocks(self.nodes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
PourTxTest().main()
|
||||
JoinSplitTest().main()
|
|
@ -41,7 +41,7 @@ bool CCoins::Spend(uint32_t nPos)
|
|||
return true;
|
||||
}
|
||||
bool CCoinsView::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; }
|
||||
bool CCoinsView::GetSerial(const uint256 &serial) const { return false; }
|
||||
bool CCoinsView::GetNullifier(const uint256 &nullifier) 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(); }
|
||||
|
@ -50,14 +50,14 @@ bool CCoinsView::BatchWrite(CCoinsMap &mapCoins,
|
|||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials) { return false; }
|
||||
CNullifiersMap &mapNullifiers) { return false; }
|
||||
bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
|
||||
|
||||
|
||||
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
|
||||
|
||||
bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); }
|
||||
bool CCoinsViewBacked::GetSerial(const uint256 &serial) const { return base->GetSerial(serial); }
|
||||
bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier) const { return base->GetNullifier(nullifier); }
|
||||
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(); }
|
||||
|
@ -67,7 +67,7 @@ bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins,
|
|||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors, mapSerials); }
|
||||
CNullifiersMap &mapNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors, mapNullifiers); }
|
||||
bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); }
|
||||
|
||||
CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
|
||||
|
@ -82,7 +82,7 @@ CCoinsViewCache::~CCoinsViewCache()
|
|||
size_t CCoinsViewCache::DynamicMemoryUsage() const {
|
||||
return memusage::DynamicUsage(cacheCoins) +
|
||||
memusage::DynamicUsage(cacheAnchors) +
|
||||
memusage::DynamicUsage(cacheSerials) +
|
||||
memusage::DynamicUsage(cacheNullifiers) +
|
||||
cachedCoinsUsage;
|
||||
}
|
||||
|
||||
|
@ -128,16 +128,16 @@ bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tr
|
|||
return true;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::GetSerial(const uint256 &serial) const {
|
||||
CSerialsMap::iterator it = cacheSerials.find(serial);
|
||||
if (it != cacheSerials.end())
|
||||
bool CCoinsViewCache::GetNullifier(const uint256 &nullifier) const {
|
||||
CNullifiersMap::iterator it = cacheNullifiers.find(nullifier);
|
||||
if (it != cacheNullifiers.end())
|
||||
return it->second.entered;
|
||||
|
||||
CSerialsCacheEntry entry;
|
||||
bool tmp = base->GetSerial(serial);
|
||||
CNullifiersCacheEntry entry;
|
||||
bool tmp = base->GetNullifier(nullifier);
|
||||
entry.entered = tmp;
|
||||
|
||||
cacheSerials.insert(std::make_pair(serial, entry));
|
||||
cacheNullifiers.insert(std::make_pair(nullifier, entry));
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ void CCoinsViewCache::PushAnchor(const ZCIncrementalMerkleTree &tree) {
|
|||
|
||||
// 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
|
||||
// because there are no joinsplits. We could get around this a
|
||||
// different way (make all blocks modify mapAnchors somehow)
|
||||
// but this is simpler to reason about.
|
||||
if (currentRoot != newrt) {
|
||||
|
@ -185,10 +185,10 @@ void CCoinsViewCache::PopAnchor(const uint256 &newrt) {
|
|||
}
|
||||
}
|
||||
|
||||
void CCoinsViewCache::SetSerial(const uint256 &serial, bool spent) {
|
||||
std::pair<CSerialsMap::iterator, bool> ret = cacheSerials.insert(std::make_pair(serial, CSerialsCacheEntry()));
|
||||
void CCoinsViewCache::SetNullifier(const uint256 &nullifier, bool spent) {
|
||||
std::pair<CNullifiersMap::iterator, bool> ret = cacheNullifiers.insert(std::make_pair(nullifier, CNullifiersCacheEntry()));
|
||||
ret.first->second.entered = spent;
|
||||
ret.first->second.flags |= CSerialsCacheEntry::DIRTY;
|
||||
ret.first->second.flags |= CNullifiersCacheEntry::DIRTY;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
|
||||
|
@ -260,7 +260,7 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
|||
const uint256 &hashBlockIn,
|
||||
const uint256 &hashAnchorIn,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials) {
|
||||
CNullifiersMap &mapNullifiers) {
|
||||
assert(!hasModifier);
|
||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
|
||||
if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
|
@ -326,29 +326,29 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
|||
mapAnchors.erase(itOld);
|
||||
}
|
||||
|
||||
for (CSerialsMap::iterator child_it = mapSerials.begin(); child_it != mapSerials.end();)
|
||||
for (CNullifiersMap::iterator child_it = mapNullifiers.begin(); child_it != mapNullifiers.end();)
|
||||
{
|
||||
if (child_it->second.flags & CSerialsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
CSerialsMap::iterator parent_it = cacheSerials.find(child_it->first);
|
||||
if (child_it->second.flags & CNullifiersCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
|
||||
CNullifiersMap::iterator parent_it = cacheNullifiers.find(child_it->first);
|
||||
|
||||
if (parent_it == cacheSerials.end()) {
|
||||
if (parent_it == cacheNullifiers.end()) {
|
||||
if (child_it->second.entered) {
|
||||
// Parent doesn't have an entry, but child has a SPENT serial.
|
||||
// Move the spent serial up.
|
||||
// Parent doesn't have an entry, but child has a SPENT nullifier.
|
||||
// Move the spent nullifier up.
|
||||
|
||||
CSerialsCacheEntry& entry = cacheSerials[child_it->first];
|
||||
CNullifiersCacheEntry& entry = cacheNullifiers[child_it->first];
|
||||
entry.entered = true;
|
||||
entry.flags = CSerialsCacheEntry::DIRTY;
|
||||
entry.flags = CNullifiersCacheEntry::DIRTY;
|
||||
}
|
||||
} else {
|
||||
if (parent_it->second.entered != child_it->second.entered) {
|
||||
parent_it->second.entered = child_it->second.entered;
|
||||
parent_it->second.flags |= CSerialsCacheEntry::DIRTY;
|
||||
parent_it->second.flags |= CNullifiersCacheEntry::DIRTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
CSerialsMap::iterator itOld = child_it++;
|
||||
mapSerials.erase(itOld);
|
||||
CNullifiersMap::iterator itOld = child_it++;
|
||||
mapNullifiers.erase(itOld);
|
||||
}
|
||||
|
||||
hashAnchor = hashAnchorIn;
|
||||
|
@ -357,10 +357,10 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
|
|||
}
|
||||
|
||||
bool CCoinsViewCache::Flush() {
|
||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashAnchor, cacheAnchors, cacheSerials);
|
||||
bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashAnchor, cacheAnchors, cacheNullifiers);
|
||||
cacheCoins.clear();
|
||||
cacheAnchors.clear();
|
||||
cacheSerials.clear();
|
||||
cacheNullifiers.clear();
|
||||
cachedCoinsUsage = 0;
|
||||
return fOk;
|
||||
}
|
||||
|
@ -385,35 +385,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();
|
||||
nResult += tx.GetJoinSplitValueIn();
|
||||
|
||||
return nResult;
|
||||
}
|
||||
|
||||
bool CCoinsViewCache::HavePourRequirements(const CTransaction& tx) const
|
||||
bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
|
||||
{
|
||||
boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates;
|
||||
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour)
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit)
|
||||
{
|
||||
BOOST_FOREACH(const uint256& serial, pour.serials)
|
||||
BOOST_FOREACH(const uint256& nullifier, joinsplit.nullifiers)
|
||||
{
|
||||
if (GetSerial(serial)) {
|
||||
// If the serial is set, this transaction
|
||||
if (GetNullifier(nullifier)) {
|
||||
// If the nullifier is set, this transaction
|
||||
// double-spends!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ZCIncrementalMerkleTree tree;
|
||||
auto it = intermediates.find(pour.anchor);
|
||||
auto it = intermediates.find(joinsplit.anchor);
|
||||
if (it != intermediates.end()) {
|
||||
tree = it->second;
|
||||
} else if (!GetAnchorAt(pour.anchor, tree)) {
|
||||
} else if (!GetAnchorAt(joinsplit.anchor, tree)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const uint256& commitment, pour.commitments)
|
||||
BOOST_FOREACH(const uint256& commitment, joinsplit.commitments)
|
||||
{
|
||||
tree.append(commitment);
|
||||
}
|
||||
|
|
32
src/coins.h
32
src/coins.h
|
@ -309,21 +309,21 @@ struct CAnchorsCacheEntry
|
|||
CAnchorsCacheEntry() : entered(false), flags(0) {}
|
||||
};
|
||||
|
||||
struct CSerialsCacheEntry
|
||||
struct CNullifiersCacheEntry
|
||||
{
|
||||
bool entered; // If the serial is spent or not
|
||||
bool entered; // If the nullifier 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) {}
|
||||
CNullifiersCacheEntry() : 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;
|
||||
typedef boost::unordered_map<uint256, CNullifiersCacheEntry, CCoinsKeyHasher> CNullifiersMap;
|
||||
|
||||
struct CCoinsStats
|
||||
{
|
||||
|
@ -346,8 +346,8 @@ public:
|
|||
//! Retrieve the tree at a particular anchored root in the chain
|
||||
virtual bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||
|
||||
//! Determine whether a serial is spent or not
|
||||
virtual bool GetSerial(const uint256 &serial) const;
|
||||
//! Determine whether a nullifier is spent or not
|
||||
virtual bool GetNullifier(const uint256 &nullifier) const;
|
||||
|
||||
//! Retrieve the CCoins (unspent transaction outputs) for a given txid
|
||||
virtual bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
|
@ -368,7 +368,7 @@ public:
|
|||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials);
|
||||
CNullifiersMap &mapNullifiers);
|
||||
|
||||
//! Calculate statistics about the unspent transaction output set
|
||||
virtual bool GetStats(CCoinsStats &stats) const;
|
||||
|
@ -387,7 +387,7 @@ protected:
|
|||
public:
|
||||
CCoinsViewBacked(CCoinsView *viewIn);
|
||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||
bool GetSerial(const uint256 &serial) const;
|
||||
bool GetNullifier(const uint256 &nullifier) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
|
@ -397,7 +397,7 @@ public:
|
|||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials);
|
||||
CNullifiersMap &mapNullifiers);
|
||||
bool GetStats(CCoinsStats &stats) const;
|
||||
};
|
||||
|
||||
|
@ -440,7 +440,7 @@ protected:
|
|||
mutable CCoinsMap cacheCoins;
|
||||
mutable uint256 hashAnchor;
|
||||
mutable CAnchorsMap cacheAnchors;
|
||||
mutable CSerialsMap cacheSerials;
|
||||
mutable CNullifiersMap cacheNullifiers;
|
||||
|
||||
/* Cached dynamic memory usage for the inner CCoins objects. */
|
||||
mutable size_t cachedCoinsUsage;
|
||||
|
@ -451,7 +451,7 @@ public:
|
|||
|
||||
// Standard CCoinsView methods
|
||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||
bool GetSerial(const uint256 &serial) const;
|
||||
bool GetNullifier(const uint256 &nullifier) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
|
@ -461,7 +461,7 @@ public:
|
|||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials);
|
||||
CNullifiersMap &mapNullifiers);
|
||||
|
||||
|
||||
// Adds the tree to mapAnchors and sets the current commitment
|
||||
|
@ -472,8 +472,8 @@ public:
|
|||
// the new current root.
|
||||
void PopAnchor(const uint256 &rt);
|
||||
|
||||
// Marks a serial as spent or not.
|
||||
void SetSerial(const uint256 &serial, bool spent);
|
||||
// Marks a nullifier as spent or not.
|
||||
void SetNullifier(const uint256 &nullifier, bool spent);
|
||||
|
||||
/**
|
||||
* Return a pointer to CCoins in the cache, or NULL if not found. This is
|
||||
|
@ -515,8 +515,8 @@ 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;
|
||||
//! Check whether all joinsplit requirements (anchors/nullifiers) are satisfied
|
||||
bool HaveJoinSplitRequirements(const CTransaction& tx) const;
|
||||
|
||||
//! Return priority of tx at height nHeight
|
||||
double GetPriority(const CTransaction &tx, int nHeight) const;
|
||||
|
|
|
@ -11,15 +11,15 @@ TEST(checktransaction_tests, check_vpub_not_both_nonzero) {
|
|||
tx.nVersion = 2;
|
||||
|
||||
{
|
||||
// Ensure that values within the pour are well-formed.
|
||||
// Ensure that values within the joinsplit are well-formed.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
newTx.vjoinsplit.push_back(JSDescription());
|
||||
|
||||
CPourTx *pourtx = &newTx.vpour[0];
|
||||
pourtx->vpub_old = 1;
|
||||
pourtx->vpub_new = 1;
|
||||
JSDescription *jsdesc = &newTx.vjoinsplit[0];
|
||||
jsdesc->vpub_old = 1;
|
||||
jsdesc->vpub_new = 1;
|
||||
|
||||
EXPECT_FALSE(CheckTransactionWithoutProofVerification(newTx, state));
|
||||
EXPECT_EQ(state.GetRejectReason(), "bad-txns-vpubs-both-nonzero");
|
||||
|
@ -55,11 +55,11 @@ CMutableTransaction GetValidTransaction() {
|
|||
// mtx.vout[0].scriptPubKey =
|
||||
mtx.vout[0].nValue = 0;
|
||||
mtx.vout[1].nValue = 0;
|
||||
mtx.vpour.resize(2);
|
||||
mtx.vpour[0].serials.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
mtx.vpour[0].serials.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
|
||||
mtx.vpour[1].serials.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
|
||||
mtx.vpour[1].serials.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000003");
|
||||
mtx.vjoinsplit.resize(2);
|
||||
mtx.vjoinsplit[0].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
mtx.vjoinsplit[0].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
|
||||
mtx.vjoinsplit[1].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
|
||||
mtx.vjoinsplit[1].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000003");
|
||||
|
||||
|
||||
// Generate an ephemeral keypair.
|
||||
|
@ -96,7 +96,7 @@ TEST(checktransaction_tests, valid_transaction) {
|
|||
|
||||
TEST(checktransaction_tests, bad_txns_vin_empty) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vpour.resize(0);
|
||||
mtx.vjoinsplit.resize(0);
|
||||
mtx.vin.resize(0);
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
@ -107,7 +107,7 @@ TEST(checktransaction_tests, bad_txns_vin_empty) {
|
|||
|
||||
TEST(checktransaction_tests, bad_txns_vout_empty) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vpour.resize(0);
|
||||
mtx.vjoinsplit.resize(0);
|
||||
mtx.vout.resize(0);
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
@ -171,7 +171,7 @@ TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_outputs) {
|
|||
TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_joinsplit) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vout[0].nValue = 1;
|
||||
mtx.vpour[0].vpub_new = MAX_MONEY;
|
||||
mtx.vjoinsplit[0].vpub_new = MAX_MONEY;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
|
@ -182,7 +182,7 @@ TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_joinsplit) {
|
|||
|
||||
TEST(checktransaction_tests, bad_txns_vpub_old_negative) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vpour[0].vpub_old = -1;
|
||||
mtx.vjoinsplit[0].vpub_old = -1;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
|
@ -193,7 +193,7 @@ TEST(checktransaction_tests, bad_txns_vpub_old_negative) {
|
|||
|
||||
TEST(checktransaction_tests, bad_txns_vpub_new_negative) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vpour[0].vpub_new = -1;
|
||||
mtx.vjoinsplit[0].vpub_new = -1;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
|
@ -204,7 +204,7 @@ TEST(checktransaction_tests, bad_txns_vpub_new_negative) {
|
|||
|
||||
TEST(checktransaction_tests, bad_txns_vpub_old_toolarge) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vpour[0].vpub_old = MAX_MONEY + 1;
|
||||
mtx.vjoinsplit[0].vpub_old = MAX_MONEY + 1;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
|
@ -215,7 +215,7 @@ TEST(checktransaction_tests, bad_txns_vpub_old_toolarge) {
|
|||
|
||||
TEST(checktransaction_tests, bad_txns_vpub_new_toolarge) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vpour[0].vpub_new = MAX_MONEY + 1;
|
||||
mtx.vjoinsplit[0].vpub_new = MAX_MONEY + 1;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
|
@ -226,8 +226,8 @@ TEST(checktransaction_tests, bad_txns_vpub_new_toolarge) {
|
|||
|
||||
TEST(checktransaction_tests, bad_txns_vpubs_both_nonzero) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vpour[0].vpub_old = 1;
|
||||
mtx.vpour[0].vpub_new = 1;
|
||||
mtx.vjoinsplit[0].vpub_old = 1;
|
||||
mtx.vjoinsplit[0].vpub_new = 1;
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
|
@ -248,43 +248,43 @@ TEST(checktransaction_tests, bad_txns_inputs_duplicate) {
|
|||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, bad_pours_serials_duplicate_same_pour) {
|
||||
TEST(checktransaction_tests, bad_joinsplits_nullifiers_duplicate_same_joinsplit) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vpour[0].serials.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
mtx.vpour[0].serials.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
mtx.vjoinsplit[0].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
mtx.vjoinsplit[0].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-pours-serials-duplicate", false)).Times(1);
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false)).Times(1);
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, bad_pours_serials_duplicate_different_pour) {
|
||||
TEST(checktransaction_tests, bad_joinsplits_nullifiers_duplicate_different_joinsplit) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
mtx.vpour[0].serials.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
mtx.vpour[1].serials.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
mtx.vjoinsplit[0].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
mtx.vjoinsplit[1].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
CTransaction tx(mtx);
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-pours-serials-duplicate", false)).Times(1);
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false)).Times(1);
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
TEST(checktransaction_tests, bad_cb_has_pours) {
|
||||
TEST(checktransaction_tests, bad_cb_has_joinsplits) {
|
||||
CMutableTransaction mtx = GetValidTransaction();
|
||||
// Make it a coinbase.
|
||||
mtx.vin.resize(1);
|
||||
mtx.vin[0].prevout.SetNull();
|
||||
|
||||
mtx.vpour.resize(1);
|
||||
mtx.vjoinsplit.resize(1);
|
||||
|
||||
CTransaction tx(mtx);
|
||||
EXPECT_TRUE(tx.IsCoinBase());
|
||||
|
||||
MockCValidationState state;
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-has-pours", false)).Times(1);
|
||||
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-has-joinsplits", false)).Times(1);
|
||||
CheckTransactionWithoutProofVerification(tx, state);
|
||||
}
|
||||
|
||||
|
@ -294,7 +294,7 @@ TEST(checktransaction_tests, bad_cb_empty_scriptsig) {
|
|||
mtx.vin.resize(1);
|
||||
mtx.vin[0].prevout.SetNull();
|
||||
|
||||
mtx.vpour.resize(0);
|
||||
mtx.vjoinsplit.resize(0);
|
||||
|
||||
CTransaction tx(mtx);
|
||||
EXPECT_TRUE(tx.IsCoinBase());
|
||||
|
|
124
src/main.cpp
124
src/main.cpp
|
@ -876,10 +876,10 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
|
|||
return false;
|
||||
} else {
|
||||
// Ensure that zk-SNARKs verify
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
if (!pour.Verify(*pzcashParams, tx.joinSplitPubKey)) {
|
||||
return state.DoS(100, error("CheckTransaction(): pour does not verify"),
|
||||
REJECT_INVALID, "bad-txns-pour-verification-failed");
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||
if (!joinsplit.Verify(*pzcashParams, tx.joinSplitPubKey)) {
|
||||
return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"),
|
||||
REJECT_INVALID, "bad-txns-joinsplit-verification-failed");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -891,11 +891,11 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
|||
// Basic checks that don't depend on any context
|
||||
|
||||
// Transactions can contain empty `vin` and `vout` so long as
|
||||
// `vpour` is non-empty.
|
||||
if (tx.vin.empty() && tx.vpour.empty())
|
||||
// `vjoinsplit` is non-empty.
|
||||
if (tx.vin.empty() && tx.vjoinsplit.empty())
|
||||
return state.DoS(10, error("CheckTransaction(): vin empty"),
|
||||
REJECT_INVALID, "bad-txns-vin-empty");
|
||||
if (tx.vout.empty() && tx.vpour.empty())
|
||||
if (tx.vout.empty() && tx.vjoinsplit.empty())
|
||||
return state.DoS(10, error("CheckTransaction(): vout empty"),
|
||||
REJECT_INVALID, "bad-txns-vout-empty");
|
||||
|
||||
|
@ -920,35 +920,35 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
|||
REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
}
|
||||
|
||||
// Ensure that pour values are well-formed
|
||||
BOOST_FOREACH(const CPourTx& pour, tx.vpour)
|
||||
// Ensure that joinsplit values are well-formed
|
||||
BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit)
|
||||
{
|
||||
if (pour.vpub_old < 0) {
|
||||
return state.DoS(100, error("CheckTransaction(): pour.vpub_old negative"),
|
||||
if (joinsplit.vpub_old < 0) {
|
||||
return state.DoS(100, error("CheckTransaction(): joinsplit.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"),
|
||||
if (joinsplit.vpub_new < 0) {
|
||||
return state.DoS(100, error("CheckTransaction(): joinsplit.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"),
|
||||
if (joinsplit.vpub_old > MAX_MONEY) {
|
||||
return state.DoS(100, error("CheckTransaction(): joinsplit.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"),
|
||||
if (joinsplit.vpub_new > MAX_MONEY) {
|
||||
return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new too high"),
|
||||
REJECT_INVALID, "bad-txns-vpub_new-toolarge");
|
||||
}
|
||||
|
||||
if (pour.vpub_new != 0 && pour.vpub_old != 0) {
|
||||
return state.DoS(100, error("CheckTransaction(): pour.vpub_new and pour.vpub_old both nonzero"),
|
||||
if (joinsplit.vpub_new != 0 && joinsplit.vpub_old != 0) {
|
||||
return state.DoS(100, error("CheckTransaction(): joinsplit.vpub_new and joinsplit.vpub_old both nonzero"),
|
||||
REJECT_INVALID, "bad-txns-vpubs-both-nonzero");
|
||||
}
|
||||
|
||||
nValueOut += pour.vpub_new;
|
||||
nValueOut += joinsplit.vpub_new;
|
||||
if (!MoneyRange(nValueOut)) {
|
||||
return state.DoS(100, error("CheckTransaction(): txout total out of range"),
|
||||
REJECT_INVALID, "bad-txns-txouttotal-toolarge");
|
||||
|
@ -966,26 +966,26 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
|||
vInOutPoints.insert(txin.prevout);
|
||||
}
|
||||
|
||||
// Check for duplicate pour serials in this transaction
|
||||
set<uint256> vPourSerials;
|
||||
BOOST_FOREACH(const CPourTx& pour, tx.vpour)
|
||||
// Check for duplicate joinsplit nullifiers in this transaction
|
||||
set<uint256> vJoinSplitNullifiers;
|
||||
BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit)
|
||||
{
|
||||
BOOST_FOREACH(const uint256& serial, pour.serials)
|
||||
BOOST_FOREACH(const uint256& nf, joinsplit.nullifiers)
|
||||
{
|
||||
if (vPourSerials.count(serial))
|
||||
return state.DoS(100, error("CheckTransaction(): duplicate serials"),
|
||||
REJECT_INVALID, "bad-pours-serials-duplicate");
|
||||
if (vJoinSplitNullifiers.count(nf))
|
||||
return state.DoS(100, error("CheckTransaction(): duplicate nullifiers"),
|
||||
REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate");
|
||||
|
||||
vPourSerials.insert(serial);
|
||||
vJoinSplitNullifiers.insert(nf);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
// There should be no joinsplits in a coinbase transaction
|
||||
if (tx.vjoinsplit.size() > 0)
|
||||
return state.DoS(100, error("CheckTransaction(): coinbase has joinsplits"),
|
||||
REJECT_INVALID, "bad-cb-has-joinsplits");
|
||||
|
||||
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 100)
|
||||
return state.DoS(100, error("CheckTransaction(): coinbase script size"),
|
||||
|
@ -998,7 +998,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio
|
|||
return state.DoS(10, error("CheckTransaction(): prevout is null"),
|
||||
REJECT_INVALID, "bad-txns-prevout-null");
|
||||
|
||||
if (tx.vpour.size() > 0) {
|
||||
if (tx.vjoinsplit.size() > 0) {
|
||||
// TODO: #966.
|
||||
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
||||
// Empty output script.
|
||||
|
@ -1104,9 +1104,9 @@ 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))
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||
if (pool.mapNullifiers.count(nf))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1144,10 +1144,10 @@ 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");
|
||||
// are the joinsplit's requirements met?
|
||||
if (!view.HaveJoinSplitRequirements(tx))
|
||||
return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"),
|
||||
REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met");
|
||||
|
||||
// Bring the best block into scope
|
||||
view.GetBestBlock();
|
||||
|
@ -1585,10 +1585,10 @@ 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);
|
||||
// spend nullifiers
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||
inputs.SetNullifier(nf, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1622,9 +1622,9 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c
|
|||
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()));
|
||||
// are the JoinSplit's requirements met?
|
||||
if (!inputs.HaveJoinSplitRequirements(tx))
|
||||
return state.Invalid(error("CheckInputs(): %s JoinSplit requirements not met", tx.GetHash().ToString()));
|
||||
|
||||
CAmount nValueIn = 0;
|
||||
CAmount nFees = 0;
|
||||
|
@ -1654,7 +1654,7 @@ bool NonContextualCheckInputs(const CTransaction& tx, CValidationState &state, c
|
|||
|
||||
}
|
||||
|
||||
nValueIn += tx.GetPourValueIn();
|
||||
nValueIn += tx.GetJoinSplitValueIn();
|
||||
if (!MoneyRange(nValueIn))
|
||||
return state.DoS(100, error("CheckInputs(): vpub_old values out of range"),
|
||||
REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
||||
|
@ -1907,10 +1907,10 @@ 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);
|
||||
// unspend nullifiers
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||
view.SetNullifier(nf, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2130,10 +2130,10 @@ 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");
|
||||
// are the JoinSplit's requirements met?
|
||||
if (!view.HaveJoinSplitRequirements(tx))
|
||||
return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"),
|
||||
REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met");
|
||||
|
||||
// Add in sigops done by pay-to-script-hash inputs;
|
||||
// this is to prevent a "rogue miner" from creating
|
||||
|
@ -2157,11 +2157,11 @@ 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.
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||
BOOST_FOREACH(const uint256 ¬e_commitment, joinsplit.commitments) {
|
||||
// Insert the note commitments into our temporary tree.
|
||||
|
||||
tree.append(bucket_commitment);
|
||||
tree.append(note_commitment);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4733,8 +4733,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||
BOOST_FOREACH(uint256 hash, vEraseQueue)
|
||||
EraseOrphanTx(hash);
|
||||
}
|
||||
// TODO: currently, prohibit pours from entering mapOrphans
|
||||
else if (fMissingInputs && tx.vpour.size() == 0)
|
||||
// TODO: currently, prohibit joinsplits from entering mapOrphans
|
||||
else if (fMissingInputs && tx.vjoinsplit.size() == 0)
|
||||
{
|
||||
AddOrphanTx(tx, pfrom->GetId());
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "tinyformat.h"
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
CPourTx::CPourTx(ZCJoinSplit& params,
|
||||
JSDescription::JSDescription(ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& anchor,
|
||||
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
|
@ -29,7 +29,7 @@ CPourTx::CPourTx(ZCJoinSplit& params,
|
|||
pubKeyHash,
|
||||
randomSeed,
|
||||
macs,
|
||||
serials,
|
||||
nullifiers,
|
||||
commitments,
|
||||
vpub_old,
|
||||
vpub_new,
|
||||
|
@ -37,7 +37,7 @@ CPourTx::CPourTx(ZCJoinSplit& params,
|
|||
);
|
||||
}
|
||||
|
||||
bool CPourTx::Verify(
|
||||
bool JSDescription::Verify(
|
||||
ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash
|
||||
) const {
|
||||
|
@ -46,7 +46,7 @@ bool CPourTx::Verify(
|
|||
pubKeyHash,
|
||||
randomSeed,
|
||||
macs,
|
||||
serials,
|
||||
nullifiers,
|
||||
commitments,
|
||||
vpub_old,
|
||||
vpub_new,
|
||||
|
@ -54,9 +54,9 @@ bool CPourTx::Verify(
|
|||
);
|
||||
}
|
||||
|
||||
uint256 CPourTx::h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const
|
||||
uint256 JSDescription::h_sig(ZCJoinSplit& params, const uint256& pubKeyHash) const
|
||||
{
|
||||
return params.h_sig(randomSeed, serials, pubKeyHash);
|
||||
return params.h_sig(randomSeed, nullifiers, pubKeyHash);
|
||||
}
|
||||
|
||||
std::string COutPoint::ToString() const
|
||||
|
@ -111,7 +111,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),
|
||||
vpour(tx.vpour), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
||||
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -126,9 +126,9 @@ void CTransaction::UpdateHash() const
|
|||
*const_cast<uint256*>(&hash) = SerializeHash(*this);
|
||||
}
|
||||
|
||||
CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), vpour(), joinSplitPubKey(), joinSplitSig() { }
|
||||
CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig() { }
|
||||
|
||||
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vpour(tx.vpour),
|
||||
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vjoinsplit(tx.vjoinsplit),
|
||||
joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
|
||||
{
|
||||
UpdateHash();
|
||||
|
@ -139,7 +139,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<std::vector<JSDescription>*>(&vjoinsplit) = tx.vjoinsplit;
|
||||
*const_cast<uint256*>(&joinSplitPubKey) = tx.joinSplitPubKey;
|
||||
*const_cast<joinsplit_sig_t*>(&joinSplitSig) = tx.joinSplitSig;
|
||||
*const_cast<uint256*>(&hash) = tx.hash;
|
||||
|
@ -156,7 +156,7 @@ CAmount CTransaction::GetValueOut() const
|
|||
throw std::runtime_error("CTransaction::GetValueOut(): value out of range");
|
||||
}
|
||||
|
||||
for (std::vector<CPourTx>::const_iterator it(vpour.begin()); it != vpour.end(); ++it)
|
||||
for (std::vector<JSDescription>::const_iterator it(vjoinsplit.begin()); it != vjoinsplit.end(); ++it)
|
||||
{
|
||||
// NB: vpub_old "takes" money from the value pool just as outputs do
|
||||
nValueOut += it->vpub_old;
|
||||
|
@ -167,16 +167,16 @@ CAmount CTransaction::GetValueOut() const
|
|||
return nValueOut;
|
||||
}
|
||||
|
||||
CAmount CTransaction::GetPourValueIn() const
|
||||
CAmount CTransaction::GetJoinSplitValueIn() const
|
||||
{
|
||||
CAmount nValue = 0;
|
||||
for (std::vector<CPourTx>::const_iterator it(vpour.begin()); it != vpour.end(); ++it)
|
||||
for (std::vector<JSDescription>::const_iterator it(vjoinsplit.begin()); it != vjoinsplit.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");
|
||||
throw std::runtime_error("CTransaction::GetJoinSplitValueIn(): value out of range");
|
||||
}
|
||||
|
||||
return nValue;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include "zcash/Zcash.h"
|
||||
#include "zcash/JoinSplit.hpp"
|
||||
|
||||
class CPourTx
|
||||
class JSDescription
|
||||
{
|
||||
public:
|
||||
// These values 'enter from' and 'exit to' the value
|
||||
|
@ -25,22 +25,22 @@ public:
|
|||
CAmount vpub_old;
|
||||
CAmount vpub_new;
|
||||
|
||||
// Pours are always anchored to a root in the bucket
|
||||
// JoinSplits are always anchored to a root in the note
|
||||
// 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
|
||||
// Nullifiers are used to prevent double-spends. They
|
||||
// are derived from the secrets placed in the note
|
||||
// and the secret spend-authority key known by the
|
||||
// spender.
|
||||
boost::array<uint256, ZC_NUM_JS_INPUTS> serials;
|
||||
boost::array<uint256, ZC_NUM_JS_INPUTS> nullifiers;
|
||||
|
||||
// Bucket commitments are introduced into the commitment
|
||||
// Note 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
|
||||
// destinations involved in the JoinSplit. The presence of
|
||||
// a commitment in the note commitment tree is required
|
||||
// to spend it.
|
||||
boost::array<uint256, ZC_NUM_JS_OUTPUTS> commitments;
|
||||
|
||||
|
@ -57,17 +57,17 @@ public:
|
|||
uint256 randomSeed;
|
||||
|
||||
// MACs
|
||||
// The verification of the pour requires these MACs
|
||||
// The verification of the JoinSplit requires these MACs
|
||||
// to be provided as an input.
|
||||
boost::array<uint256, ZC_NUM_JS_INPUTS> macs;
|
||||
|
||||
// Pour proof
|
||||
// This is a zk-SNARK which ensures that this pour is valid.
|
||||
// JoinSplit proof
|
||||
// This is a zk-SNARK which ensures that this JoinSplit is valid.
|
||||
boost::array<unsigned char, ZKSNARK_PROOF_SIZE> proof;
|
||||
|
||||
CPourTx(): vpub_old(0), vpub_new(0) { }
|
||||
JSDescription(): vpub_old(0), vpub_new(0) { }
|
||||
|
||||
CPourTx(ZCJoinSplit& params,
|
||||
JSDescription(ZCJoinSplit& params,
|
||||
const uint256& pubKeyHash,
|
||||
const uint256& rt,
|
||||
const boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS>& inputs,
|
||||
|
@ -76,7 +76,7 @@ public:
|
|||
CAmount vpub_new
|
||||
);
|
||||
|
||||
// Verifies that the pour proof is correct.
|
||||
// Verifies that the JoinSplit proof is correct.
|
||||
bool Verify(ZCJoinSplit& params, const uint256& pubKeyHash) const;
|
||||
|
||||
// Returns the calculated h_sig
|
||||
|
@ -89,7 +89,7 @@ public:
|
|||
READWRITE(vpub_old);
|
||||
READWRITE(vpub_new);
|
||||
READWRITE(anchor);
|
||||
READWRITE(serials);
|
||||
READWRITE(nullifiers);
|
||||
READWRITE(commitments);
|
||||
READWRITE(ephemeralKey);
|
||||
READWRITE(ciphertexts);
|
||||
|
@ -98,13 +98,13 @@ public:
|
|||
READWRITE(proof);
|
||||
}
|
||||
|
||||
friend bool operator==(const CPourTx& a, const CPourTx& b)
|
||||
friend bool operator==(const JSDescription& a, const JSDescription& b)
|
||||
{
|
||||
return (
|
||||
a.vpub_old == b.vpub_old &&
|
||||
a.vpub_new == b.vpub_new &&
|
||||
a.anchor == b.anchor &&
|
||||
a.serials == b.serials &&
|
||||
a.nullifiers == b.nullifiers &&
|
||||
a.commitments == b.commitments &&
|
||||
a.ephemeralKey == b.ephemeralKey &&
|
||||
a.ciphertexts == b.ciphertexts &&
|
||||
|
@ -114,7 +114,7 @@ public:
|
|||
);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CPourTx& a, const CPourTx& b)
|
||||
friend bool operator!=(const JSDescription& a, const JSDescription& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
@ -303,7 +303,7 @@ public:
|
|||
const std::vector<CTxIn> vin;
|
||||
const std::vector<CTxOut> vout;
|
||||
const uint32_t nLockTime;
|
||||
const std::vector<CPourTx> vpour;
|
||||
const std::vector<JSDescription> vjoinsplit;
|
||||
const uint256 joinSplitPubKey;
|
||||
const joinsplit_sig_t joinSplitSig;
|
||||
|
||||
|
@ -325,8 +325,8 @@ public:
|
|||
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
|
||||
READWRITE(*const_cast<uint32_t*>(&nLockTime));
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(*const_cast<std::vector<CPourTx>*>(&vpour));
|
||||
if (vpour.size() > 0) {
|
||||
READWRITE(*const_cast<std::vector<JSDescription>*>(&vjoinsplit));
|
||||
if (vjoinsplit.size() > 0) {
|
||||
READWRITE(*const_cast<uint256*>(&joinSplitPubKey));
|
||||
READWRITE(*const_cast<joinsplit_sig_t*>(&joinSplitSig));
|
||||
}
|
||||
|
@ -348,8 +348,8 @@ 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;
|
||||
// Return sum of JoinSplit vpub_new
|
||||
CAmount GetJoinSplitValueIn() const;
|
||||
|
||||
// Compute priority, given priority of inputs and (optionally) tx size
|
||||
double ComputePriority(double dPriorityInputs, unsigned int nTxSize=0) const;
|
||||
|
@ -382,7 +382,7 @@ struct CMutableTransaction
|
|||
std::vector<CTxIn> vin;
|
||||
std::vector<CTxOut> vout;
|
||||
uint32_t nLockTime;
|
||||
std::vector<CPourTx> vpour;
|
||||
std::vector<JSDescription> vjoinsplit;
|
||||
uint256 joinSplitPubKey;
|
||||
CTransaction::joinsplit_sig_t joinSplitSig;
|
||||
|
||||
|
@ -399,8 +399,8 @@ struct CMutableTransaction
|
|||
READWRITE(vout);
|
||||
READWRITE(nLockTime);
|
||||
if (nVersion >= 2) {
|
||||
READWRITE(vpour);
|
||||
if (vpour.size() > 0) {
|
||||
READWRITE(vjoinsplit);
|
||||
if (vjoinsplit.size() > 0) {
|
||||
READWRITE(joinSplitPubKey);
|
||||
READWRITE(joinSplitSig);
|
||||
}
|
||||
|
|
|
@ -91,10 +91,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "estimatepriority", 0 },
|
||||
{ "prioritisetransaction", 1 },
|
||||
{ "prioritisetransaction", 2 },
|
||||
{ "zcrawpour", 1 },
|
||||
{ "zcrawpour", 2 },
|
||||
{ "zcrawpour", 3 },
|
||||
{ "zcrawpour", 4 },
|
||||
{ "zcrawjoinsplit", 1 },
|
||||
{ "zcrawjoinsplit", 2 },
|
||||
{ "zcrawjoinsplit", 3 },
|
||||
{ "zcrawjoinsplit", 4 },
|
||||
{ "zcbenchmark", 1 },
|
||||
{ "getblocksubsidy", 0}
|
||||
};
|
||||
|
|
|
@ -90,48 +90,48 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
|
|||
}
|
||||
entry.push_back(Pair("vout", vout));
|
||||
|
||||
Array vpour;
|
||||
for (unsigned int i = 0; i < tx.vpour.size(); i++) {
|
||||
const CPourTx& pourtx = tx.vpour[i];
|
||||
Object pour;
|
||||
Array vjoinsplit;
|
||||
for (unsigned int i = 0; i < tx.vjoinsplit.size(); i++) {
|
||||
const JSDescription& jsdescription = tx.vjoinsplit[i];
|
||||
Object joinsplit;
|
||||
|
||||
pour.push_back(Pair("anchor", pourtx.anchor.GetHex()));
|
||||
joinsplit.push_back(Pair("anchor", jsdescription.anchor.GetHex()));
|
||||
|
||||
{
|
||||
Array serials;
|
||||
BOOST_FOREACH(const uint256 serial, pourtx.serials) {
|
||||
serials.push_back(serial.GetHex());
|
||||
Array nullifiers;
|
||||
BOOST_FOREACH(const uint256 nf, jsdescription.nullifiers) {
|
||||
nullifiers.push_back(nf.GetHex());
|
||||
}
|
||||
pour.push_back(Pair("serials", serials));
|
||||
joinsplit.push_back(Pair("nullifiers", nullifiers));
|
||||
}
|
||||
|
||||
{
|
||||
Array commitments;
|
||||
BOOST_FOREACH(const uint256 commitment, pourtx.commitments) {
|
||||
BOOST_FOREACH(const uint256 commitment, jsdescription.commitments) {
|
||||
commitments.push_back(commitment.GetHex());
|
||||
}
|
||||
pour.push_back(Pair("commitments", commitments));
|
||||
joinsplit.push_back(Pair("commitments", commitments));
|
||||
}
|
||||
|
||||
{
|
||||
Array macs;
|
||||
BOOST_FOREACH(const uint256 mac, pourtx.macs) {
|
||||
BOOST_FOREACH(const uint256 mac, jsdescription.macs) {
|
||||
macs.push_back(mac.GetHex());
|
||||
}
|
||||
pour.push_back(Pair("macs", macs));
|
||||
joinsplit.push_back(Pair("macs", macs));
|
||||
}
|
||||
|
||||
pour.push_back(Pair("vpub_old", ValueFromAmount(pourtx.vpub_old)));
|
||||
pour.push_back(Pair("vpub_new", ValueFromAmount(pourtx.vpub_new)));
|
||||
joinsplit.push_back(Pair("vpub_old", ValueFromAmount(jsdescription.vpub_old)));
|
||||
joinsplit.push_back(Pair("vpub_new", ValueFromAmount(jsdescription.vpub_new)));
|
||||
|
||||
// TODO: #808
|
||||
uint256 pubKeyHash;
|
||||
pour.push_back(Pair("valid", pourtx.Verify(*pzcashParams, pubKeyHash)));
|
||||
joinsplit.push_back(Pair("valid", jsdescription.Verify(*pzcashParams, pubKeyHash)));
|
||||
|
||||
vpour.push_back(pour);
|
||||
vjoinsplit.push_back(joinsplit);
|
||||
}
|
||||
|
||||
entry.push_back(Pair("vpour", vpour));
|
||||
entry.push_back(Pair("vjoinsplit", vjoinsplit));
|
||||
|
||||
if (!hashBlock.IsNull()) {
|
||||
entry.push_back(Pair("blockhash", hashBlock.GetHex()));
|
||||
|
|
|
@ -379,7 +379,7 @@ static const CRPCCommand vRPCCommands[] =
|
|||
{ "wallet", "walletpassphrase", &walletpassphrase, true },
|
||||
{ "wallet", "zcbenchmark", &zc_benchmark, true },
|
||||
{ "wallet", "zcrawkeygen", &zc_raw_keygen, true },
|
||||
{ "wallet", "zcrawpour", &zc_raw_pour, true },
|
||||
{ "wallet", "zcrawjoinsplit", &zc_raw_joinsplit, true },
|
||||
{ "wallet", "zcrawreceive", &zc_raw_receive, true }
|
||||
#endif // ENABLE_WALLET
|
||||
};
|
||||
|
|
|
@ -210,7 +210,7 @@ extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHe
|
|||
extern json_spirit::Value resendwallettransactions(const json_spirit::Array& params, bool fHelp);
|
||||
extern json_spirit::Value zc_benchmark(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_joinsplit(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
|
||||
|
|
|
@ -1072,7 +1072,7 @@ public:
|
|||
// Serialize nLockTime
|
||||
::Serialize(s, txTo.nLockTime, nType, nVersion);
|
||||
|
||||
// Serialize vpour
|
||||
// Serialize vjoinsplit
|
||||
if (txTo.nVersion >= 2) {
|
||||
//
|
||||
// SIGHASH_* functions will hash portions of
|
||||
|
@ -1080,8 +1080,8 @@ public:
|
|||
// keeps the JoinSplit cryptographically bound
|
||||
// to the transaction.
|
||||
//
|
||||
::Serialize(s, txTo.vpour, nType, nVersion);
|
||||
if (txTo.vpour.size() > 0) {
|
||||
::Serialize(s, txTo.vjoinsplit, nType, nVersion);
|
||||
if (txTo.vjoinsplit.size() > 0) {
|
||||
::Serialize(s, txTo.joinSplitPubKey, nType, nVersion);
|
||||
|
||||
CTransaction::joinsplit_sig_t nullSig = {};
|
||||
|
|
|
@ -24,7 +24,7 @@ class CCoinsViewTest : public CCoinsView
|
|||
uint256 hashBestAnchor_;
|
||||
std::map<uint256, CCoins> map_;
|
||||
std::map<uint256, ZCIncrementalMerkleTree> mapAnchors_;
|
||||
std::map<uint256, bool> mapSerials_;
|
||||
std::map<uint256, bool> mapNullifiers_;
|
||||
|
||||
public:
|
||||
CCoinsViewTest() {
|
||||
|
@ -47,11 +47,11 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
bool GetSerial(const uint256 &serial) const
|
||||
bool GetNullifier(const uint256 &nf) const
|
||||
{
|
||||
std::map<uint256, bool>::const_iterator it = mapSerials_.find(serial);
|
||||
std::map<uint256, bool>::const_iterator it = mapNullifiers_.find(nf);
|
||||
|
||||
if (it == mapSerials_.end()) {
|
||||
if (it == mapNullifiers_.end()) {
|
||||
return false;
|
||||
} else {
|
||||
// The map shouldn't contain any false entries.
|
||||
|
@ -88,7 +88,7 @@ public:
|
|||
const uint256& hashBlock,
|
||||
const uint256& hashAnchor,
|
||||
CAnchorsMap& mapAnchors,
|
||||
CSerialsMap& mapSerials)
|
||||
CNullifiersMap& mapNullifiers)
|
||||
{
|
||||
for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end(); ) {
|
||||
map_[it->first] = it->second.coins;
|
||||
|
@ -109,17 +109,17 @@ public:
|
|||
}
|
||||
mapAnchors.erase(it++);
|
||||
}
|
||||
for (CSerialsMap::iterator it = mapSerials.begin(); it != mapSerials.end(); ) {
|
||||
for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end(); ) {
|
||||
if (it->second.entered) {
|
||||
mapSerials_[it->first] = true;
|
||||
mapNullifiers_[it->first] = true;
|
||||
} else {
|
||||
mapSerials_.erase(it->first);
|
||||
mapNullifiers_.erase(it->first);
|
||||
}
|
||||
mapSerials.erase(it++);
|
||||
mapNullifiers.erase(it++);
|
||||
}
|
||||
mapCoins.clear();
|
||||
mapAnchors.clear();
|
||||
mapSerials.clear();
|
||||
mapNullifiers.clear();
|
||||
hashBestBlock_ = hashBlock;
|
||||
hashBestAnchor_ = hashAnchor;
|
||||
return true;
|
||||
|
@ -138,7 +138,7 @@ public:
|
|||
// Manually recompute the dynamic usage of the whole data, and compare it.
|
||||
size_t ret = memusage::DynamicUsage(cacheCoins) +
|
||||
memusage::DynamicUsage(cacheAnchors) +
|
||||
memusage::DynamicUsage(cacheSerials);
|
||||
memusage::DynamicUsage(cacheNullifiers);
|
||||
for (CCoinsMap::iterator it = cacheCoins.begin(); it != cacheCoins.end(); it++) {
|
||||
ret += memusage::DynamicUsage(it->second.coins);
|
||||
}
|
||||
|
@ -163,28 +163,28 @@ uint256 appendRandomCommitment(ZCIncrementalMerkleTree &tree)
|
|||
|
||||
BOOST_FIXTURE_TEST_SUITE(coins_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(serials_test)
|
||||
BOOST_AUTO_TEST_CASE(nullifiers_test)
|
||||
{
|
||||
CCoinsViewTest base;
|
||||
CCoinsViewCacheTest cache(&base);
|
||||
|
||||
uint256 myserial = GetRandHash();
|
||||
uint256 nf = GetRandHash();
|
||||
|
||||
BOOST_CHECK(!cache.GetSerial(myserial));
|
||||
cache.SetSerial(myserial, true);
|
||||
BOOST_CHECK(cache.GetSerial(myserial));
|
||||
BOOST_CHECK(!cache.GetNullifier(nf));
|
||||
cache.SetNullifier(nf, true);
|
||||
BOOST_CHECK(cache.GetNullifier(nf));
|
||||
cache.Flush();
|
||||
|
||||
CCoinsViewCacheTest cache2(&base);
|
||||
|
||||
BOOST_CHECK(cache2.GetSerial(myserial));
|
||||
cache2.SetSerial(myserial, false);
|
||||
BOOST_CHECK(!cache2.GetSerial(myserial));
|
||||
BOOST_CHECK(cache2.GetNullifier(nf));
|
||||
cache2.SetNullifier(nf, false);
|
||||
BOOST_CHECK(!cache2.GetNullifier(nf));
|
||||
cache2.Flush();
|
||||
|
||||
CCoinsViewCacheTest cache3(&base);
|
||||
|
||||
BOOST_CHECK(!cache3.GetSerial(myserial));
|
||||
BOOST_CHECK(!cache3.GetNullifier(nf));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(anchors_flush_test)
|
||||
|
@ -217,80 +217,80 @@ BOOST_AUTO_TEST_CASE(anchors_flush_test)
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(chained_pours)
|
||||
BOOST_AUTO_TEST_CASE(chained_joinsplits)
|
||||
{
|
||||
CCoinsViewTest base;
|
||||
CCoinsViewCacheTest cache(&base);
|
||||
|
||||
ZCIncrementalMerkleTree tree;
|
||||
|
||||
CPourTx ptx1;
|
||||
ptx1.anchor = tree.root();
|
||||
ptx1.commitments[0] = appendRandomCommitment(tree);
|
||||
ptx1.commitments[1] = appendRandomCommitment(tree);
|
||||
JSDescription js1;
|
||||
js1.anchor = tree.root();
|
||||
js1.commitments[0] = appendRandomCommitment(tree);
|
||||
js1.commitments[1] = appendRandomCommitment(tree);
|
||||
|
||||
// Although it's not possible given our assumptions, if
|
||||
// two pours create the same treestate twice, we should
|
||||
// two joinsplits create the same treestate twice, we should
|
||||
// still be able to anchor to it.
|
||||
CPourTx ptx1b;
|
||||
ptx1b.anchor = tree.root();
|
||||
ptx1b.commitments[0] = ptx1.commitments[0];
|
||||
ptx1b.commitments[1] = ptx1.commitments[1];
|
||||
JSDescription js1b;
|
||||
js1b.anchor = tree.root();
|
||||
js1b.commitments[0] = js1.commitments[0];
|
||||
js1b.commitments[1] = js1.commitments[1];
|
||||
|
||||
CPourTx ptx2;
|
||||
CPourTx ptx3;
|
||||
JSDescription js2;
|
||||
JSDescription js3;
|
||||
|
||||
ptx2.anchor = tree.root();
|
||||
ptx3.anchor = tree.root();
|
||||
js2.anchor = tree.root();
|
||||
js3.anchor = tree.root();
|
||||
|
||||
ptx2.commitments[0] = appendRandomCommitment(tree);
|
||||
ptx2.commitments[1] = appendRandomCommitment(tree);
|
||||
js2.commitments[0] = appendRandomCommitment(tree);
|
||||
js2.commitments[1] = appendRandomCommitment(tree);
|
||||
|
||||
ptx3.commitments[0] = appendRandomCommitment(tree);
|
||||
ptx3.commitments[1] = appendRandomCommitment(tree);
|
||||
js3.commitments[0] = appendRandomCommitment(tree);
|
||||
js3.commitments[1] = appendRandomCommitment(tree);
|
||||
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
mtx.vpour.push_back(ptx2);
|
||||
mtx.vjoinsplit.push_back(js2);
|
||||
|
||||
BOOST_CHECK(!cache.HavePourRequirements(mtx));
|
||||
BOOST_CHECK(!cache.HaveJoinSplitRequirements(mtx));
|
||||
}
|
||||
|
||||
{
|
||||
// ptx2 is trying to anchor to ptx1 but ptx1
|
||||
// js2 is trying to anchor to js1 but js1
|
||||
// appears afterwards -- not a permitted ordering
|
||||
CMutableTransaction mtx;
|
||||
mtx.vpour.push_back(ptx2);
|
||||
mtx.vpour.push_back(ptx1);
|
||||
mtx.vjoinsplit.push_back(js2);
|
||||
mtx.vjoinsplit.push_back(js1);
|
||||
|
||||
BOOST_CHECK(!cache.HavePourRequirements(mtx));
|
||||
BOOST_CHECK(!cache.HaveJoinSplitRequirements(mtx));
|
||||
}
|
||||
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
mtx.vpour.push_back(ptx1);
|
||||
mtx.vpour.push_back(ptx2);
|
||||
mtx.vjoinsplit.push_back(js1);
|
||||
mtx.vjoinsplit.push_back(js2);
|
||||
|
||||
BOOST_CHECK(cache.HavePourRequirements(mtx));
|
||||
BOOST_CHECK(cache.HaveJoinSplitRequirements(mtx));
|
||||
}
|
||||
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
mtx.vpour.push_back(ptx1);
|
||||
mtx.vpour.push_back(ptx2);
|
||||
mtx.vpour.push_back(ptx3);
|
||||
mtx.vjoinsplit.push_back(js1);
|
||||
mtx.vjoinsplit.push_back(js2);
|
||||
mtx.vjoinsplit.push_back(js3);
|
||||
|
||||
BOOST_CHECK(cache.HavePourRequirements(mtx));
|
||||
BOOST_CHECK(cache.HaveJoinSplitRequirements(mtx));
|
||||
}
|
||||
|
||||
{
|
||||
CMutableTransaction mtx;
|
||||
mtx.vpour.push_back(ptx1);
|
||||
mtx.vpour.push_back(ptx1b);
|
||||
mtx.vpour.push_back(ptx2);
|
||||
mtx.vpour.push_back(ptx3);
|
||||
mtx.vjoinsplit.push_back(js1);
|
||||
mtx.vjoinsplit.push_back(js1b);
|
||||
mtx.vjoinsplit.push_back(js2);
|
||||
mtx.vjoinsplit.push_back(js3);
|
||||
|
||||
BOOST_CHECK(cache.HavePourRequirements(mtx));
|
||||
BOOST_CHECK(cache.HaveJoinSplitRequirements(mtx));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,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);
|
||||
int joinsplits = (insecure_rand() % 4);
|
||||
for (int in = 0; in < ins; in++) {
|
||||
tx.vin.push_back(CTxIn());
|
||||
CTxIn &txin = tx.vin.back();
|
||||
|
@ -121,26 +121,26 @@ void static RandomTransaction(CMutableTransaction &tx, bool fSingle) {
|
|||
RandomScript(txout.scriptPubKey);
|
||||
}
|
||||
if (tx.nVersion >= 2) {
|
||||
for (int pour = 0; pour < pours; pour++) {
|
||||
CPourTx pourtx;
|
||||
for (int js = 0; js < joinsplits; js++) {
|
||||
JSDescription jsdesc;
|
||||
if (insecure_rand() % 2 == 0) {
|
||||
pourtx.vpub_old = insecure_rand() % 100000000;
|
||||
jsdesc.vpub_old = insecure_rand() % 100000000;
|
||||
} else {
|
||||
pourtx.vpub_new = insecure_rand() % 100000000;
|
||||
jsdesc.vpub_new = insecure_rand() % 100000000;
|
||||
}
|
||||
|
||||
pourtx.anchor = GetRandHash();
|
||||
pourtx.serials[0] = GetRandHash();
|
||||
pourtx.serials[1] = GetRandHash();
|
||||
pourtx.ephemeralKey = GetRandHash();
|
||||
pourtx.randomSeed = GetRandHash();
|
||||
randombytes_buf(pourtx.ciphertexts[0].begin(), pourtx.ciphertexts[0].size());
|
||||
randombytes_buf(pourtx.ciphertexts[1].begin(), pourtx.ciphertexts[1].size());
|
||||
randombytes_buf(pourtx.proof.begin(), pourtx.proof.size());
|
||||
pourtx.macs[0] = GetRandHash();
|
||||
pourtx.macs[1] = GetRandHash();
|
||||
jsdesc.anchor = GetRandHash();
|
||||
jsdesc.nullifiers[0] = GetRandHash();
|
||||
jsdesc.nullifiers[1] = GetRandHash();
|
||||
jsdesc.ephemeralKey = GetRandHash();
|
||||
jsdesc.randomSeed = GetRandHash();
|
||||
randombytes_buf(jsdesc.ciphertexts[0].begin(), jsdesc.ciphertexts[0].size());
|
||||
randombytes_buf(jsdesc.ciphertexts[1].begin(), jsdesc.ciphertexts[1].size());
|
||||
randombytes_buf(jsdesc.proof.begin(), jsdesc.proof.size());
|
||||
jsdesc.macs[0] = GetRandHash();
|
||||
jsdesc.macs[1] = GetRandHash();
|
||||
|
||||
tx.vpour.push_back(pourtx);
|
||||
tx.vjoinsplit.push_back(jsdesc);
|
||||
}
|
||||
|
||||
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
|
||||
|
|
|
@ -291,9 +291,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
|
|||
return dummyTransactions;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
|
||||
BOOST_AUTO_TEST_CASE(test_basic_joinsplit_verification)
|
||||
{
|
||||
// We only check that pours are constructed properly
|
||||
// We only check that joinsplits 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.
|
||||
|
@ -330,7 +330,7 @@ BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
|
|||
|
||||
auto witness = merkleTree.witness();
|
||||
|
||||
// create CPourTx
|
||||
// create JSDescription
|
||||
uint256 pubKeyHash;
|
||||
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
|
||||
libzcash::JSInput(witness, note, k),
|
||||
|
@ -342,28 +342,28 @@ BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
|
|||
};
|
||||
|
||||
{
|
||||
CPourTx pourtx(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
|
||||
BOOST_CHECK(pourtx.Verify(*p, pubKeyHash));
|
||||
JSDescription jsdesc(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
|
||||
BOOST_CHECK(jsdesc.Verify(*p, pubKeyHash));
|
||||
|
||||
CDataStream ss(SER_DISK, CLIENT_VERSION);
|
||||
ss << pourtx;
|
||||
ss << jsdesc;
|
||||
|
||||
CPourTx pourtx_deserialized;
|
||||
ss >> pourtx_deserialized;
|
||||
JSDescription jsdesc_deserialized;
|
||||
ss >> jsdesc_deserialized;
|
||||
|
||||
BOOST_CHECK(pourtx_deserialized == pourtx);
|
||||
BOOST_CHECK(pourtx_deserialized.Verify(*p, pubKeyHash));
|
||||
BOOST_CHECK(jsdesc_deserialized == jsdesc);
|
||||
BOOST_CHECK(jsdesc_deserialized.Verify(*p, pubKeyHash));
|
||||
}
|
||||
|
||||
{
|
||||
// Ensure that the balance equation is working.
|
||||
BOOST_CHECK_THROW(CPourTx(*p, pubKeyHash, rt, inputs, outputs, 10, 0), std::invalid_argument);
|
||||
BOOST_CHECK_THROW(CPourTx(*p, pubKeyHash, rt, inputs, outputs, 0, 10), std::invalid_argument);
|
||||
BOOST_CHECK_THROW(JSDescription(*p, pubKeyHash, rt, inputs, outputs, 10, 0), std::invalid_argument);
|
||||
BOOST_CHECK_THROW(JSDescription(*p, pubKeyHash, rt, inputs, outputs, 0, 10), std::invalid_argument);
|
||||
}
|
||||
|
||||
{
|
||||
// Ensure that it won't verify if the root is changed.
|
||||
auto test = CPourTx(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
|
||||
auto test = JSDescription(*p, pubKeyHash, rt, inputs, outputs, 0, 0);
|
||||
test.anchor = GetRandHash();
|
||||
BOOST_CHECK(!test.Verify(*p, pubKeyHash));
|
||||
}
|
||||
|
@ -371,20 +371,20 @@ BOOST_AUTO_TEST_CASE(test_basic_pour_verification)
|
|||
delete p;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
||||
BOOST_AUTO_TEST_CASE(test_simple_joinsplit_invalidity)
|
||||
{
|
||||
CMutableTransaction tx;
|
||||
tx.nVersion = 2;
|
||||
{
|
||||
// Ensure that empty vin/vout remain invalid without
|
||||
// pours.
|
||||
// joinsplits.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
|
||||
crypto_sign_keypair(newTx.joinSplitPubKey.begin(), joinSplitPrivKey);
|
||||
|
||||
// No pours, vin and vout, means it should be invalid.
|
||||
// No joinsplits, vin and vout, means it should be invalid.
|
||||
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vin-empty");
|
||||
|
||||
|
@ -393,11 +393,11 @@ BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
|||
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vout-empty");
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
CPourTx *pourtx = &newTx.vpour[0];
|
||||
newTx.vjoinsplit.push_back(JSDescription());
|
||||
JSDescription *jsdesc = &newTx.vjoinsplit[0];
|
||||
|
||||
pourtx->serials[0] = GetRandHash();
|
||||
pourtx->serials[1] = GetRandHash();
|
||||
jsdesc->nullifiers[0] = GetRandHash();
|
||||
jsdesc->nullifiers[1] = GetRandHash();
|
||||
|
||||
BOOST_CHECK(!CheckTransactionWithoutProofVerification(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-invalid-joinsplit-signature");
|
||||
|
@ -418,78 +418,78 @@ BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
|||
BOOST_CHECK(CheckTransactionWithoutProofVerification(newTx, state));
|
||||
}
|
||||
{
|
||||
// Ensure that values within the pour are well-formed.
|
||||
// Ensure that values within the joinsplit are well-formed.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
newTx.vjoinsplit.push_back(JSDescription());
|
||||
|
||||
CPourTx *pourtx = &newTx.vpour[0];
|
||||
pourtx->vpub_old = -1;
|
||||
JSDescription *jsdesc = &newTx.vjoinsplit[0];
|
||||
jsdesc->vpub_old = -1;
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_old-negative");
|
||||
|
||||
pourtx->vpub_old = MAX_MONEY + 1;
|
||||
jsdesc->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;
|
||||
jsdesc->vpub_old = 0;
|
||||
jsdesc->vpub_new = -1;
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-txns-vpub_new-negative");
|
||||
|
||||
pourtx->vpub_new = MAX_MONEY + 1;
|
||||
jsdesc->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;
|
||||
jsdesc->vpub_new = (MAX_MONEY / 2) + 10;
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
newTx.vjoinsplit.push_back(JSDescription());
|
||||
|
||||
CPourTx *pourtx2 = &newTx.vpour[1];
|
||||
pourtx2->vpub_new = (MAX_MONEY / 2) + 10;
|
||||
JSDescription *jsdesc2 = &newTx.vjoinsplit[1];
|
||||
jsdesc2->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.
|
||||
// Ensure that nullifiers are never duplicated within a transaction.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
CPourTx *pourtx = &newTx.vpour[0];
|
||||
newTx.vjoinsplit.push_back(JSDescription());
|
||||
JSDescription *jsdesc = &newTx.vjoinsplit[0];
|
||||
|
||||
pourtx->serials[0] = GetRandHash();
|
||||
pourtx->serials[1] = pourtx->serials[0];
|
||||
jsdesc->nullifiers[0] = GetRandHash();
|
||||
jsdesc->nullifiers[1] = jsdesc->nullifiers[0];
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-pours-serials-duplicate");
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-joinsplits-nullifiers-duplicate");
|
||||
|
||||
pourtx->serials[1] = GetRandHash();
|
||||
jsdesc->nullifiers[1] = GetRandHash();
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
CPourTx *pourtx2 = &newTx.vpour[1];
|
||||
newTx.vjoinsplit.push_back(JSDescription());
|
||||
JSDescription *jsdesc2 = &newTx.vjoinsplit[1];
|
||||
|
||||
pourtx2->serials[0] = GetRandHash();
|
||||
pourtx2->serials[1] = pourtx->serials[0];
|
||||
jsdesc2->nullifiers[0] = GetRandHash();
|
||||
jsdesc2->nullifiers[1] = jsdesc->nullifiers[0];
|
||||
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-pours-serials-duplicate");
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-joinsplits-nullifiers-duplicate");
|
||||
}
|
||||
{
|
||||
// Ensure that coinbase transactions do not have pours.
|
||||
// Ensure that coinbase transactions do not have joinsplits.
|
||||
CMutableTransaction newTx(tx);
|
||||
CValidationState state;
|
||||
|
||||
newTx.vpour.push_back(CPourTx());
|
||||
CPourTx *pourtx = &newTx.vpour[0];
|
||||
pourtx->serials[0] = GetRandHash();
|
||||
pourtx->serials[1] = GetRandHash();
|
||||
newTx.vjoinsplit.push_back(JSDescription());
|
||||
JSDescription *jsdesc = &newTx.vjoinsplit[0];
|
||||
jsdesc->nullifiers[0] = GetRandHash();
|
||||
jsdesc->nullifiers[1] = GetRandHash();
|
||||
|
||||
newTx.vin.push_back(CTxIn(uint256(), -1));
|
||||
|
||||
|
@ -498,7 +498,7 @@ BOOST_AUTO_TEST_CASE(test_simple_pour_invalidity)
|
|||
BOOST_CHECK(finalNewTx.IsCoinBase());
|
||||
}
|
||||
BOOST_CHECK(!CheckTransaction(newTx, state));
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-pours");
|
||||
BOOST_CHECK(state.GetRejectReason() == "bad-cb-has-joinsplits");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
24
src/txdb.cpp
24
src/txdb.cpp
|
@ -18,7 +18,7 @@
|
|||
using namespace std;
|
||||
|
||||
static const char DB_ANCHOR = 'A';
|
||||
static const char DB_SERIAL = 's';
|
||||
static const char DB_NULLIFIER = 's';
|
||||
static const char DB_COINS = 'c';
|
||||
static const char DB_BLOCK_FILES = 'f';
|
||||
static const char DB_TXINDEX = 't';
|
||||
|
@ -43,11 +43,11 @@ void static BatchWriteAnchor(CLevelDBBatch &batch,
|
|||
}
|
||||
}
|
||||
|
||||
void static BatchWriteSerial(CLevelDBBatch &batch, const uint256 &serial, const bool &entered) {
|
||||
void static BatchWriteNullifier(CLevelDBBatch &batch, const uint256 &nf, const bool &entered) {
|
||||
if (!entered)
|
||||
batch.Erase(make_pair(DB_SERIAL, serial));
|
||||
batch.Erase(make_pair(DB_NULLIFIER, nf));
|
||||
else
|
||||
batch.Write(make_pair(DB_SERIAL, serial), true);
|
||||
batch.Write(make_pair(DB_NULLIFIER, nf), true);
|
||||
}
|
||||
|
||||
void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) {
|
||||
|
@ -81,9 +81,9 @@ bool CCoinsViewDB::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree)
|
|||
return read;
|
||||
}
|
||||
|
||||
bool CCoinsViewDB::GetSerial(const uint256 &serial) const {
|
||||
bool CCoinsViewDB::GetNullifier(const uint256 &nf) const {
|
||||
bool spent = false;
|
||||
bool read = db.Read(make_pair(DB_SERIAL, serial), spent);
|
||||
bool read = db.Read(make_pair(DB_NULLIFIER, nf), spent);
|
||||
|
||||
return read;
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins,
|
|||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials) {
|
||||
CNullifiersMap &mapNullifiers) {
|
||||
CLevelDBBatch batch;
|
||||
size_t count = 0;
|
||||
size_t changed = 0;
|
||||
|
@ -137,13 +137,13 @@ bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins,
|
|||
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);
|
||||
for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end();) {
|
||||
if (it->second.flags & CNullifiersCacheEntry::DIRTY) {
|
||||
BatchWriteNullifier(batch, it->first, it->second.entered);
|
||||
// TODO: changed++?
|
||||
}
|
||||
CSerialsMap::iterator itOld = it++;
|
||||
mapSerials.erase(itOld);
|
||||
CNullifiersMap::iterator itOld = it++;
|
||||
mapNullifiers.erase(itOld);
|
||||
}
|
||||
|
||||
if (!hashBlock.IsNull())
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
CCoinsViewDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false);
|
||||
|
||||
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const;
|
||||
bool GetSerial(const uint256 &serial) const;
|
||||
bool GetNullifier(const uint256 &nf) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
uint256 GetBestBlock() const;
|
||||
|
@ -44,7 +44,7 @@ public:
|
|||
const uint256 &hashBlock,
|
||||
const uint256 &hashAnchor,
|
||||
CAnchorsMap &mapAnchors,
|
||||
CSerialsMap &mapSerials);
|
||||
CNullifiersMap &mapNullifiers);
|
||||
bool GetStats(CCoinsStats &stats) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -99,9 +99,9 @@ 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;
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||
mapNullifiers[nf] = &tx;
|
||||
}
|
||||
}
|
||||
nTransactionsUpdated++;
|
||||
|
@ -148,9 +148,9 @@ 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);
|
||||
BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) {
|
||||
BOOST_FOREACH(const uint256& nf, joinsplit.nullifiers) {
|
||||
mapNullifiers.erase(nf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,8 +200,8 @@ void CTxMemPool::removeWithAnchor(const uint256 &invalidRoot)
|
|||
|
||||
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) {
|
||||
BOOST_FOREACH(const JSDescription& joinsplit, tx.vjoinsplit) {
|
||||
if (joinsplit.anchor == invalidRoot) {
|
||||
transactionsToRemove.push_back(tx);
|
||||
break;
|
||||
}
|
||||
|
@ -230,10 +230,10 @@ 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()) {
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||
std::map<uint256, const CTransaction*>::iterator it = mapNullifiers.find(nf);
|
||||
if (it != mapNullifiers.end()) {
|
||||
const CTransaction &txConflict = *it->second;
|
||||
if (txConflict != tx)
|
||||
{
|
||||
|
@ -317,20 +317,20 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||
|
||||
boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates;
|
||||
|
||||
BOOST_FOREACH(const CPourTx &pour, tx.vpour) {
|
||||
BOOST_FOREACH(const uint256 &serial, pour.serials) {
|
||||
assert(!pcoins->GetSerial(serial));
|
||||
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
|
||||
BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) {
|
||||
assert(!pcoins->GetNullifier(nf));
|
||||
}
|
||||
|
||||
ZCIncrementalMerkleTree tree;
|
||||
auto it = intermediates.find(pour.anchor);
|
||||
auto it = intermediates.find(joinsplit.anchor);
|
||||
if (it != intermediates.end()) {
|
||||
tree = it->second;
|
||||
} else {
|
||||
assert(pcoins->GetAnchorAt(pour.anchor, tree));
|
||||
assert(pcoins->GetAnchorAt(joinsplit.anchor, tree));
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const uint256& commitment, pour.commitments)
|
||||
BOOST_FOREACH(const uint256& commitment, joinsplit.commitments)
|
||||
{
|
||||
tree.append(commitment);
|
||||
}
|
||||
|
@ -370,7 +370,7 @@ 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++) {
|
||||
for (std::map<uint256, const CTransaction*>::const_iterator it = mapNullifiers.begin(); it != mapNullifiers.end(); it++) {
|
||||
uint256 hash = it->second->GetHash();
|
||||
map<uint256, CTxMemPoolEntry>::const_iterator it2 = mapTx.find(hash);
|
||||
const CTransaction& tx = it2->second.GetTx();
|
||||
|
@ -484,11 +484,11 @@ 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))
|
||||
bool CCoinsViewMemPool::GetNullifier(const uint256 &nf) const {
|
||||
if (mempool.mapNullifiers.count(nf))
|
||||
return true;
|
||||
|
||||
return base->GetSerial(serial);
|
||||
return base->GetNullifier(nf);
|
||||
}
|
||||
|
||||
bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) const {
|
||||
|
|
|
@ -98,7 +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, const CTransaction*> mapNullifiers;
|
||||
std::map<uint256, std::pair<double, CAmount> > mapDeltas;
|
||||
|
||||
CTxMemPool(const CFeeRate& _minRelayFee);
|
||||
|
@ -177,7 +177,7 @@ protected:
|
|||
|
||||
public:
|
||||
CCoinsViewMemPool(CCoinsView *baseIn, CTxMemPool &mempoolIn);
|
||||
bool GetSerial(const uint256 &txid) const;
|
||||
bool GetNullifier(const uint256 &txid) const;
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
};
|
||||
|
|
|
@ -2393,18 +2393,18 @@ Value zc_benchmark(const json_spirit::Array& params, bool fHelp)
|
|||
pzcashParams->loadProvingKey();
|
||||
}
|
||||
|
||||
CPourTx* samplejoinsplit = NULL;
|
||||
JSDescription* samplejoinsplit = NULL;
|
||||
|
||||
if (benchmarktype == "verifyjoinsplit") {
|
||||
uint256 pubKeyHash;
|
||||
uint256 anchor = ZCIncrementalMerkleTree().root();
|
||||
samplejoinsplit = new CPourTx(*pzcashParams,
|
||||
pubKeyHash,
|
||||
anchor,
|
||||
{JSInput(), JSInput()},
|
||||
{JSOutput(), JSOutput()},
|
||||
0,
|
||||
0);
|
||||
samplejoinsplit = new JSDescription(*pzcashParams,
|
||||
pubKeyHash,
|
||||
anchor,
|
||||
{JSInput(), JSInput()},
|
||||
{JSOutput(), JSOutput()},
|
||||
0,
|
||||
0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < samplecount; i++) {
|
||||
|
@ -2448,14 +2448,14 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
|
|||
|
||||
if (fHelp || params.size() != 2) {
|
||||
throw runtime_error(
|
||||
"zcrawreceive zcsecretkey encryptedbucket\n"
|
||||
"zcrawreceive zcsecretkey encryptednote\n"
|
||||
"\n"
|
||||
"Decrypts encryptedbucket and checks if the coin commitments\n"
|
||||
"Decrypts encryptednote 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"
|
||||
" \"note\": noteplaintext,\n"
|
||||
" \"exists\": exists\n"
|
||||
"}\n"
|
||||
);
|
||||
|
@ -2474,7 +2474,7 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
|
|||
uint256 h_sig;
|
||||
|
||||
{
|
||||
CDataStream ssData(ParseHexV(params[1], "encrypted_bucket"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
CDataStream ssData(ParseHexV(params[1], "encrypted_note"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
try {
|
||||
ssData >> nonce;
|
||||
ssData >> epk;
|
||||
|
@ -2482,7 +2482,7 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
|
|||
ssData >> h_sig;
|
||||
} catch(const std::exception &) {
|
||||
throw runtime_error(
|
||||
"encrypted_bucket could not be decoded"
|
||||
"encrypted_note could not be decoded"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2503,7 +2503,7 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
|
|||
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
|
||||
uint256 anchor;
|
||||
uint256 commitment = decrypted_note.cm();
|
||||
pwalletMain->WitnessBucketCommitment(
|
||||
pwalletMain->WitnessNoteCommitment(
|
||||
{commitment},
|
||||
witnesses,
|
||||
anchor
|
||||
|
@ -2514,14 +2514,14 @@ Value zc_raw_receive(const json_spirit::Array& params, bool fHelp)
|
|||
|
||||
Object result;
|
||||
result.push_back(Pair("amount", ValueFromAmount(decrypted_note.value)));
|
||||
result.push_back(Pair("bucket", HexStr(ss.begin(), ss.end())));
|
||||
result.push_back(Pair("note", HexStr(ss.begin(), ss.end())));
|
||||
result.push_back(Pair("exists", (bool) witnesses[0]));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
||||
Value zc_raw_joinsplit(const json_spirit::Array& params, bool fHelp)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp)) {
|
||||
return Value::null;
|
||||
|
@ -2529,11 +2529,11 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
|
||||
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"
|
||||
"zcrawjoinsplit rawtx inputs outputs vpub_old vpub_new\n"
|
||||
" inputs: a JSON object mapping {note: zcsecretkey, ...}\n"
|
||||
" outputs: a JSON object mapping {zcaddr: value, ...}\n"
|
||||
"\n"
|
||||
"Splices a Pour into rawtx. Inputs are unilaterally confidential.\n"
|
||||
"Splices a joinsplit 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"
|
||||
|
@ -2544,8 +2544,8 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
"payments in-band on the blockchain.)\n"
|
||||
"\n"
|
||||
"Output: {\n"
|
||||
" \"encryptedbucket1\": enc1,\n"
|
||||
" \"encryptedbucket2\": enc2,\n"
|
||||
" \"encryptednote1\": enc1,\n"
|
||||
" \"encryptednote2\": enc2,\n"
|
||||
" \"rawtxn\": rawtxout\n"
|
||||
"}\n"
|
||||
);
|
||||
|
@ -2569,8 +2569,8 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
if (params[4].get_real() != 0.0)
|
||||
vpub_new = AmountFromValue(params[4]);
|
||||
|
||||
std::vector<JSInput> vpourin;
|
||||
std::vector<JSOutput> vpourout;
|
||||
std::vector<JSInput> vjsin;
|
||||
std::vector<JSOutput> vjsout;
|
||||
std::vector<Note> notes;
|
||||
std::vector<SpendingKey> keys;
|
||||
std::vector<uint256> commitments;
|
||||
|
@ -2585,7 +2585,7 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
NotePlaintext npt;
|
||||
|
||||
{
|
||||
CDataStream ssData(ParseHexV(s.name_, "bucket"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
CDataStream ssData(ParseHexV(s.name_, "note"), SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssData >> npt;
|
||||
}
|
||||
|
||||
|
@ -2597,7 +2597,7 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
|
||||
uint256 anchor;
|
||||
std::vector<boost::optional<ZCIncrementalWitness>> witnesses;
|
||||
pwalletMain->WitnessBucketCommitment(commitments, witnesses, anchor);
|
||||
pwalletMain->WitnessNoteCommitment(commitments, witnesses, anchor);
|
||||
|
||||
assert(witnesses.size() == notes.size());
|
||||
assert(notes.size() == keys.size());
|
||||
|
@ -2606,16 +2606,16 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
for (size_t i = 0; i < witnesses.size(); i++) {
|
||||
if (!witnesses[i]) {
|
||||
throw runtime_error(
|
||||
"pour input could not be found in tree"
|
||||
"joinsplit input could not be found in tree"
|
||||
);
|
||||
}
|
||||
|
||||
vpourin.push_back(JSInput(*witnesses[i], notes[i], keys[i]));
|
||||
vjsin.push_back(JSInput(*witnesses[i], notes[i], keys[i]));
|
||||
}
|
||||
}
|
||||
|
||||
while (vpourin.size() < ZC_NUM_JS_INPUTS) {
|
||||
vpourin.push_back(JSInput());
|
||||
while (vjsin.size() < ZC_NUM_JS_INPUTS) {
|
||||
vjsin.push_back(JSInput());
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const Pair& s, outputs)
|
||||
|
@ -2624,16 +2624,16 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
PaymentAddress addrTo = pubaddr.Get();
|
||||
CAmount nAmount = AmountFromValue(s.value_);
|
||||
|
||||
vpourout.push_back(JSOutput(addrTo, nAmount));
|
||||
vjsout.push_back(JSOutput(addrTo, nAmount));
|
||||
}
|
||||
|
||||
while (vpourout.size() < ZC_NUM_JS_OUTPUTS) {
|
||||
vpourout.push_back(JSOutput());
|
||||
while (vjsout.size() < ZC_NUM_JS_OUTPUTS) {
|
||||
vjsout.push_back(JSOutput());
|
||||
}
|
||||
|
||||
// TODO
|
||||
if (vpourout.size() != ZC_NUM_JS_INPUTS || vpourin.size() != ZC_NUM_JS_OUTPUTS) {
|
||||
throw runtime_error("unsupported pour input/output counts");
|
||||
if (vjsout.size() != ZC_NUM_JS_INPUTS || vjsin.size() != ZC_NUM_JS_OUTPUTS) {
|
||||
throw runtime_error("unsupported joinsplit input/output counts");
|
||||
}
|
||||
|
||||
uint256 joinSplitPubKey;
|
||||
|
@ -2644,17 +2644,17 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
mtx.nVersion = 2;
|
||||
mtx.joinSplitPubKey = joinSplitPubKey;
|
||||
|
||||
CPourTx pourtx(*pzcashParams,
|
||||
joinSplitPubKey,
|
||||
anchor,
|
||||
{vpourin[0], vpourin[1]},
|
||||
{vpourout[0], vpourout[1]},
|
||||
vpub_old,
|
||||
vpub_new);
|
||||
JSDescription jsdesc(*pzcashParams,
|
||||
joinSplitPubKey,
|
||||
anchor,
|
||||
{vjsin[0], vjsin[1]},
|
||||
{vjsout[0], vjsout[1]},
|
||||
vpub_old,
|
||||
vpub_new);
|
||||
|
||||
assert(pourtx.Verify(*pzcashParams, joinSplitPubKey));
|
||||
assert(jsdesc.Verify(*pzcashParams, joinSplitPubKey));
|
||||
|
||||
mtx.vpour.push_back(pourtx);
|
||||
mtx.vjoinsplit.push_back(jsdesc);
|
||||
|
||||
// TODO: #966.
|
||||
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
||||
|
@ -2683,30 +2683,30 @@ Value zc_raw_pour(const json_spirit::Array& params, bool fHelp)
|
|||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << rawTx;
|
||||
|
||||
std::string encryptedBucket1;
|
||||
std::string encryptedBucket2;
|
||||
std::string encryptedNote1;
|
||||
std::string encryptedNote2;
|
||||
{
|
||||
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss2 << ((unsigned char) 0x00);
|
||||
ss2 << pourtx.ephemeralKey;
|
||||
ss2 << pourtx.ciphertexts[0];
|
||||
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey);
|
||||
ss2 << jsdesc.ephemeralKey;
|
||||
ss2 << jsdesc.ciphertexts[0];
|
||||
ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey);
|
||||
|
||||
encryptedBucket1 = HexStr(ss2.begin(), ss2.end());
|
||||
encryptedNote1 = HexStr(ss2.begin(), ss2.end());
|
||||
}
|
||||
{
|
||||
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss2 << ((unsigned char) 0x01);
|
||||
ss2 << pourtx.ephemeralKey;
|
||||
ss2 << pourtx.ciphertexts[1];
|
||||
ss2 << pourtx.h_sig(*pzcashParams, joinSplitPubKey);
|
||||
ss2 << jsdesc.ephemeralKey;
|
||||
ss2 << jsdesc.ciphertexts[1];
|
||||
ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey);
|
||||
|
||||
encryptedBucket2 = HexStr(ss2.begin(), ss2.end());
|
||||
encryptedNote2 = HexStr(ss2.begin(), ss2.end());
|
||||
}
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("encryptedbucket1", encryptedBucket1));
|
||||
result.push_back(Pair("encryptedbucket2", encryptedBucket2));
|
||||
result.push_back(Pair("encryptednote1", encryptedNote1));
|
||||
result.push_back(Pair("encryptednote2", encryptedNote2));
|
||||
result.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end())));
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1051,9 +1051,9 @@ bool CWalletTx::WriteToDisk(CWalletDB *pwalletdb)
|
|||
return pwalletdb->WriteTx(GetHash(), *this);
|
||||
}
|
||||
|
||||
void CWallet::WitnessBucketCommitment(std::vector<uint256> commitments,
|
||||
std::vector<boost::optional<ZCIncrementalWitness>>& witnesses,
|
||||
uint256 &final_anchor)
|
||||
void CWallet::WitnessNoteCommitment(std::vector<uint256> commitments,
|
||||
std::vector<boost::optional<ZCIncrementalWitness>>& witnesses,
|
||||
uint256 &final_anchor)
|
||||
{
|
||||
witnesses.resize(commitments.size());
|
||||
CBlockIndex* pindex = chainActive.Genesis();
|
||||
|
@ -1065,21 +1065,21 @@ void CWallet::WitnessBucketCommitment(std::vector<uint256> commitments,
|
|||
|
||||
BOOST_FOREACH(const CTransaction& tx, block.vtx)
|
||||
{
|
||||
BOOST_FOREACH(const CPourTx& pour, tx.vpour)
|
||||
BOOST_FOREACH(const JSDescription& jsdesc, tx.vjoinsplit)
|
||||
{
|
||||
BOOST_FOREACH(const uint256 &bucket_commitment, pour.commitments)
|
||||
BOOST_FOREACH(const uint256 ¬e_commitment, jsdesc.commitments)
|
||||
{
|
||||
tree.append(bucket_commitment);
|
||||
tree.append(note_commitment);
|
||||
|
||||
BOOST_FOREACH(boost::optional<ZCIncrementalWitness>& wit, witnesses) {
|
||||
if (wit) {
|
||||
wit->append(bucket_commitment);
|
||||
wit->append(note_commitment);
|
||||
}
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
BOOST_FOREACH(uint256& commitment, commitments) {
|
||||
if (bucket_commitment == commitment) {
|
||||
if (note_commitment == commitment) {
|
||||
witnesses.at(i) = tree.witness();
|
||||
}
|
||||
i++;
|
||||
|
|
|
@ -616,7 +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);
|
||||
void WitnessBucketCommitment(
|
||||
void WitnessNoteCommitment(
|
||||
std::vector<uint256> commitments,
|
||||
std::vector<boost::optional<ZCIncrementalWitness>>& witnesses,
|
||||
uint256 &final_anchor);
|
||||
|
|
|
@ -76,20 +76,20 @@ double benchmark_create_joinsplit()
|
|||
uint256 anchor = ZCIncrementalMerkleTree().root();
|
||||
|
||||
timer_start();
|
||||
CPourTx pourtx(*pzcashParams,
|
||||
pubKeyHash,
|
||||
anchor,
|
||||
{JSInput(), JSInput()},
|
||||
{JSOutput(), JSOutput()},
|
||||
0,
|
||||
0);
|
||||
JSDescription jsdesc(*pzcashParams,
|
||||
pubKeyHash,
|
||||
anchor,
|
||||
{JSInput(), JSInput()},
|
||||
{JSOutput(), JSOutput()},
|
||||
0,
|
||||
0);
|
||||
double ret = timer_stop();
|
||||
|
||||
assert(pourtx.Verify(*pzcashParams, pubKeyHash));
|
||||
assert(jsdesc.Verify(*pzcashParams, pubKeyHash));
|
||||
return ret;
|
||||
}
|
||||
|
||||
double benchmark_verify_joinsplit(const CPourTx &joinsplit)
|
||||
double benchmark_verify_joinsplit(const JSDescription &joinsplit)
|
||||
{
|
||||
timer_start();
|
||||
uint256 pubKeyHash;
|
||||
|
|
|
@ -8,7 +8,7 @@ extern double benchmark_sleep();
|
|||
extern double benchmark_parameter_loading();
|
||||
extern double benchmark_create_joinsplit();
|
||||
extern double benchmark_solve_equihash();
|
||||
extern double benchmark_verify_joinsplit(const CPourTx &joinsplit);
|
||||
extern double benchmark_verify_joinsplit(const JSDescription &joinsplit);
|
||||
extern double benchmark_verify_equihash();
|
||||
extern double benchmark_large_tx();
|
||||
|
||||
|
|
Loading…
Reference in New Issue