Major updates integration from all upstreams

This commit is contained in:
miketout
2018-09-18 14:33:53 -07:00
396 changed files with 25517 additions and 6854 deletions

View File

@@ -26,7 +26,7 @@ void expect_deser_same(const T& expected)
CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION);
ss2 << object;
ASSERT_TRUE(serialized_size == ss2.size());
ASSERT_EQ(serialized_size, ss2.size());
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), serialized_size) == 0);
}
@@ -45,7 +45,7 @@ void expect_test_vector(T& v, const U& expected)
std::string raw = v.get_str();
CDataStream ss2(ParseHex(raw), SER_NETWORK, PROTOCOL_VERSION);
ASSERT_TRUE(ss1.size() == ss2.size());
ASSERT_EQ(ss1.size(), ss2.size());
ASSERT_TRUE(memcmp(&*ss1.begin(), &*ss2.begin(), ss1.size()) == 0);
#endif
}

View File

@@ -1,5 +1,6 @@
#include "gmock/gmock.h"
#include "crypto/common.h"
#include "key.h"
#include "pubkey.h"
#include "zcash/JoinSplit.hpp"
#include "util.h"
@@ -7,6 +8,8 @@
#include <libsnark/common/default_types/r1cs_ppzksnark_pp.hpp>
#include <libsnark/zk_proof_systems/ppzksnark/r1cs_ppzksnark/r1cs_ppzksnark.hpp>
#include "librustzcash.h"
struct ECCryptoClosure
{
ECCVerifyHandle handle;
@@ -18,13 +21,36 @@ ZCJoinSplit* params;
int main(int argc, char **argv) {
assert(init_and_check_sodium() != -1);
ECC_Start();
libsnark::default_r1cs_ppzksnark_pp::init_public_params();
libsnark::inhibit_profiling_info = true;
libsnark::inhibit_profiling_counters = true;
boost::filesystem::path pk_path = ZC_GetParamsDir() / "sprout-proving.key";
boost::filesystem::path vk_path = ZC_GetParamsDir() / "sprout-verifying.key";
params = ZCJoinSplit::Prepared(vk_path.string(), pk_path.string());
boost::filesystem::path sapling_spend = ZC_GetParamsDir() / "sapling-spend.params";
boost::filesystem::path sapling_output = ZC_GetParamsDir() / "sapling-output.params";
boost::filesystem::path sprout_groth16 = ZC_GetParamsDir() / "sprout-groth16.params";
std::string sapling_spend_str = sapling_spend.string();
std::string sapling_output_str = sapling_output.string();
std::string sprout_groth16_str = sprout_groth16.string();
librustzcash_init_zksnark_params(
sapling_spend_str.c_str(),
"8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c",
sapling_output_str.c_str(),
"657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028",
sprout_groth16_str.c_str(),
"e9b238411bd6c0ec4791e9d04245ec350c9c5744f5610dfcce4365d5ca49dfefd5054e371842b3f88fa1b9d7e8e075249b3ebabd167fa8b0f3161292d36c180a"
);
testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
auto ret = RUN_ALL_TESTS();
ECC_Stop();
return ret;
}

View File

@@ -68,27 +68,99 @@ TEST(CheckBlock, BlockSproutRejectsBadVersion) {
}
TEST(ContextualCheckBlock, BadCoinbaseHeight) {
SelectParams(CBaseChainParams::MAIN);
class ContextualCheckBlockTest : public ::testing::Test {
protected:
virtual void SetUp() {
SelectParams(CBaseChainParams::MAIN);
}
// Create a block with no height in scriptSig
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
virtual void TearDown() {
// Revert to test default. No-op on mainnet params.
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
// Returns a valid but empty mutable transaction at block height 1.
CMutableTransaction GetFirstBlockCoinbaseTx() {
CMutableTransaction mtx;
// No inputs.
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
// Set height to 1.
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
// Give it a single zero-valued, always-valid output.
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
// Give it a Founder's Reward vout for height 1.
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
return mtx;
}
// Expects a height-1 block containing a given transaction to pass
// ContextualCheckBlock. This is used in accepting (Sprout-Sprout,
// Overwinter-Overwinter, ...) tests. You should not call it without
// calling a SCOPED_TRACE macro first to usefully label any failures.
void ExpectValidBlockFromTx(const CTransaction& tx) {
// Create a block and add the transaction to it.
CBlock block;
block.vtx.push_back(tx);
// Set the previous block index to the genesis block.
CBlockIndex indexPrev {Params().GenesisBlock()};
// We now expect this to be a valid block.
MockCValidationState state;
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
}
// Expects a height-1 block containing a given transaction to fail
// ContextualCheckBlock. This is used in rejecting (Sprout-Overwinter,
// Overwinter-Sprout, ...) tests. You should not call it without
// calling a SCOPED_TRACE macro first to usefully label any failures.
void ExpectInvalidBlockFromTx(const CTransaction& tx, int level, std::string reason) {
// Create a block and add the transaction to it.
CBlock block;
block.vtx.push_back(tx);
// Set the previous block index to the genesis block.
CBlockIndex indexPrev {Params().GenesisBlock()};
// We now expect this to be an invalid block, for the given reason.
MockCValidationState state;
EXPECT_CALL(state, DoS(level, false, REJECT_INVALID, reason, false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
}
};
TEST_F(ContextualCheckBlockTest, BadCoinbaseHeight) {
// Put a transaction in a block with no height in scriptSig
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
mtx.vin[0].scriptSig = CScript() << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
CTransaction tx {mtx};
mtx.vout.pop_back(); // remove the FR output
CBlock block;
block.vtx.push_back(tx);
block.vtx.push_back(mtx);
// Treating block as genesis should pass
MockCValidationState state;
EXPECT_TRUE(ContextualCheckBlock(block, state, NULL));
// Give the transaction a Founder's Reward vout
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
// Treating block as non-genesis should fail
mtx.vout.push_back(CTxOut(GetBlockSubsidy(1, Params().GetConsensus())/5, Params().GetFoundersRewardScriptAtHeight(1)));
CTransaction tx2 {mtx};
block.vtx[0] = tx2;
CBlock prev;
@@ -111,124 +183,142 @@ TEST(ContextualCheckBlock, BadCoinbaseHeight) {
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
}
// Test that a block evaluated under Sprout rules cannot contain Overwinter transactions.
// TEST PLAN: first, check that each ruleset accepts its own transaction type.
// Currently (May 2018) this means we'll test Sprout-Sprout,
// Overwinter-Overwinter, and Sapling-Sapling.
// Test block evaluated under Sprout rules will accept Sprout transactions.
// This test assumes that mainnet Overwinter activation is at least height 2.
TEST(ContextualCheckBlock, BlockSproutRulesRejectOverwinterTx) {
SelectParams(CBaseChainParams::MAIN);
TEST_F(ContextualCheckBlockTest, BlockSproutRulesAcceptSproutTx) {
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
}
// Test block evaluated under Sprout rules will accept Sprout transactions
TEST(ContextualCheckBlock, BlockSproutRulesAcceptSproutTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
// Make it a Sprout transaction w/o JoinSplits
mtx.fOverwintered = false;
mtx.nVersion = 1;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
SCOPED_TRACE("BlockSproutRulesAcceptSproutTx");
ExpectValidBlockFromTx(CTransaction(mtx));
}
// Test block evaluated under Overwinter rules will accept Overwinter transactions
TEST(ContextualCheckBlock, BlockOverwinterRulesAcceptOverwinterTx) {
// Test block evaluated under Overwinter rules will accept Overwinter transactions.
TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesAcceptOverwinterTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
mtx.vout.push_back(CTxOut(
GetBlockSubsidy(1, Params().GetConsensus())/5,
Params().GetFoundersRewardScriptAtHeight(1)));
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
// Make it an Overwinter transaction
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
EXPECT_TRUE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
SCOPED_TRACE("BlockOverwinterRulesAcceptOverwinterTx");
ExpectValidBlockFromTx(CTransaction(mtx));
}
// Test that a block evaluated under Sapling rules can contain Sapling transactions.
TEST_F(ContextualCheckBlockTest, BlockSaplingRulesAcceptSaplingTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1);
// Test block evaluated under Overwinter rules will reject Sprout transactions
TEST(ContextualCheckBlock, BlockOverwinterRulesRejectSproutTx) {
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
// Make it a Sapling transaction
mtx.fOverwintered = true;
mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
SCOPED_TRACE("BlockSaplingRulesAcceptSaplingTx");
ExpectValidBlockFromTx(CTransaction(mtx));
}
// TEST PLAN: next, check that each ruleset will not accept other transaction
// types. Currently (May 2018) this means we'll test Sprout-Overwinter,
// Sprout-Sapling, Overwinter-Sprout, Overwinter-Sapling, Sapling-Sprout, and
// Sapling-Overwinter.
// Test that a block evaluated under Sprout rules cannot contain non-Sprout
// transactions which require Overwinter to be active. This test assumes that
// mainnet Overwinter activation is at least height 2.
TEST_F(ContextualCheckBlockTest, BlockSproutRulesRejectOtherTx) {
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
// Make it an Overwinter transaction
mtx.fOverwintered = true;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
{
SCOPED_TRACE("BlockSproutRulesRejectOverwinterTx");
ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "tx-overwinter-not-active");
}
// Make it a Sapling transaction
mtx.fOverwintered = true;
mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
{
SCOPED_TRACE("BlockSproutRulesRejectSaplingTx");
ExpectInvalidBlockFromTx(CTransaction(mtx), 0, "tx-overwinter-not-active");
}
};
// Test block evaluated under Overwinter rules cannot contain non-Overwinter
// transactions.
TEST_F(ContextualCheckBlockTest, BlockOverwinterRulesRejectOtherTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
CMutableTransaction mtx;
mtx.vin.resize(1);
mtx.vin[0].prevout.SetNull();
mtx.vin[0].scriptSig = CScript() << 1 << OP_0;
mtx.vout.resize(1);
mtx.vout[0].scriptPubKey = CScript() << OP_TRUE;
mtx.vout[0].nValue = 0;
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
// Set the version to Sprout+JoinSplit (but nJoinSplit will be 0).
mtx.nVersion = 2;
CTransaction tx {mtx};
CBlock block;
block.vtx.push_back(tx);
{
SCOPED_TRACE("BlockOverwinterRulesRejectSproutTx");
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwinter-active");
}
MockCValidationState state;
CBlockIndex indexPrev {Params().GenesisBlock()};
// Make it a Sapling transaction
mtx.fOverwintered = true;
mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-active", false)).Times(1);
EXPECT_FALSE(ContextualCheckBlock(block, state, &indexPrev));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
{
SCOPED_TRACE("BlockOverwinterRulesRejectSaplingTx");
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-overwinter-tx-version-group-id");
}
}
// Test block evaluated under Sapling rules cannot contain non-Sapling transactions.
TEST_F(ContextualCheckBlockTest, BlockSaplingRulesRejectOtherTx) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, 1);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, 1);
CMutableTransaction mtx = GetFirstBlockCoinbaseTx();
// Set the version to Sprout+JoinSplit (but nJoinSplit will be 0).
mtx.nVersion = 2;
{
SCOPED_TRACE("BlockSaplingRulesRejectSproutTx");
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "tx-overwinter-active");
}
// Make it an Overwinter transaction
mtx.fOverwintered = true;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
{
SCOPED_TRACE("BlockSaplingRulesRejectOverwinterTx");
ExpectInvalidBlockFromTx(CTransaction(mtx), 100, "bad-sapling-tx-version-group-id");
}
}

View File

@@ -6,6 +6,8 @@
#include "primitives/transaction.h"
#include "consensus/validation.h"
extern ZCJoinSplit* params;
TEST(checktransaction_tests, check_vpub_not_both_nonzero) {
CMutableTransaction tx;
tx.nVersion = 2;
@@ -43,6 +45,7 @@ public:
MOCK_CONST_METHOD0(GetRejectReason, std::string());
};
void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranchId);
CMutableTransaction GetValidTransaction() {
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
@@ -63,7 +66,11 @@ CMutableTransaction GetValidTransaction() {
mtx.vjoinsplit[1].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
mtx.vjoinsplit[1].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000003");
CreateJoinSplitSignature(mtx, consensusBranchId);
return mtx;
}
void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranchId) {
// Generate an ephemeral keypair.
uint256 joinSplitPubKey;
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
@@ -86,7 +93,6 @@ CMutableTransaction GetValidTransaction() {
dataToBeSigned.begin(), 32,
joinSplitPrivKey
) == 0);
return mtx;
}
TEST(checktransaction_tests, valid_transaction) {
@@ -129,7 +135,8 @@ TEST(checktransaction_tests, bad_txns_vout_empty) {
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_oversize) {
TEST(checktransaction_tests, BadTxnsOversize) {
SelectParams(CBaseChainParams::REGTEST);
CMutableTransaction mtx = GetValidTransaction();
mtx.vin[0].scriptSig = CScript();
@@ -153,10 +160,100 @@ TEST(checktransaction_tests, bad_txns_oversize) {
CTransaction tx(mtx);
ASSERT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), 100202);
// Passes non-contextual checks...
MockCValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
// ... but fails contextual ones!
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1);
EXPECT_FALSE(ContextualCheckTransaction(tx, state, 1, 100));
}
{
// But should be fine again once Sapling activates!
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
mtx.fOverwintered = true;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
mtx.nVersion = SAPLING_TX_VERSION;
// Change the proof types (which requires re-signing the JoinSplit data)
mtx.vjoinsplit[0].proof = libzcash::GrothProof();
mtx.vjoinsplit[1].proof = libzcash::GrothProof();
CreateJoinSplitSignature(mtx, NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId);
CTransaction tx(mtx);
EXPECT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), 103713);
MockCValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
EXPECT_TRUE(ContextualCheckTransaction(tx, state, 1, 100));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
}
TEST(checktransaction_tests, OversizeSaplingTxns) {
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
CMutableTransaction mtx = GetValidTransaction();
mtx.fOverwintered = true;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
mtx.nVersion = SAPLING_TX_VERSION;
// Change the proof types (which requires re-signing the JoinSplit data)
mtx.vjoinsplit[0].proof = libzcash::GrothProof();
mtx.vjoinsplit[1].proof = libzcash::GrothProof();
CreateJoinSplitSignature(mtx, NetworkUpgradeInfo[Consensus::UPGRADE_SAPLING].nBranchId);
// Transaction just under the limit
mtx.vin[0].scriptSig = CScript();
std::vector<unsigned char> vchData(520);
for (unsigned int i = 0; i < 3809; ++i)
mtx.vin[0].scriptSig << vchData << OP_DROP;
std::vector<unsigned char> vchDataRemainder(453);
mtx.vin[0].scriptSig << vchDataRemainder << OP_DROP;
mtx.vin[0].scriptSig << OP_1;
{
CTransaction tx(mtx);
EXPECT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), MAX_TX_SIZE_AFTER_SAPLING - 1);
CValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
}
// Transaction equal to the limit
mtx.vin[1].scriptSig << OP_1;
{
CTransaction tx(mtx);
EXPECT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), MAX_TX_SIZE_AFTER_SAPLING);
CValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
}
// Transaction just over the limit
mtx.vin[1].scriptSig << OP_1;
{
CTransaction tx(mtx);
EXPECT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), MAX_TX_SIZE_AFTER_SAPLING + 1);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
EXPECT_FALSE(CheckTransactionWithoutProofVerification(tx, state));
}
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(checktransaction_tests, bad_txns_vout_negative) {
@@ -193,6 +290,54 @@ TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_outputs) {
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, ValueBalanceNonZero) {
CMutableTransaction mtx = GetValidTransaction();
mtx.valueBalance = 10;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-nonzero", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, PositiveValueBalanceTooLarge) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vShieldedSpend.resize(1);
mtx.valueBalance = MAX_MONEY + 1;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, NegativeValueBalanceTooLarge) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vShieldedSpend.resize(1);
mtx.valueBalance = -(MAX_MONEY + 1);
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-valuebalance-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, ValueBalanceOverflowsTotal) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vShieldedSpend.resize(1);
mtx.vout[0].nValue = 1;
mtx.valueBalance = -MAX_MONEY;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_joinsplit) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vout[0].nValue = 1;
@@ -361,7 +506,7 @@ TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) {
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
ContextualCheckTransaction(tx, state, 0, 100);
}
@@ -394,14 +539,14 @@ TEST(checktransaction_tests, non_canonical_ed25519_signature) {
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
ContextualCheckTransaction(tx, state, 0, 100);
}
TEST(checktransaction_tests, OverwinterConstructors) {
CMutableTransaction mtx;
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 20;
@@ -432,7 +577,7 @@ TEST(checktransaction_tests, OverwinterConstructors) {
TEST(checktransaction_tests, OverwinterSerialization) {
CMutableTransaction mtx;
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 99;
@@ -496,7 +641,7 @@ TEST(checktransaction_tests, OverwinterValidTx) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
CTransaction tx(mtx);
@@ -508,7 +653,7 @@ TEST(checktransaction_tests, OverwinterExpiryHeight) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
@@ -567,6 +712,58 @@ class UNSAFE_CTransaction : public CTransaction {
UNSAFE_CTransaction(const CMutableTransaction &tx) : CTransaction(tx, true) {}
};
TEST(checktransaction_tests, SaplingSproutInputSumsTooLarge) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = true;
mtx.nVersion = SAPLING_TX_VERSION;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
{
// create JSDescription
uint256 rt;
uint256 joinSplitPubKey;
std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
libzcash::JSInput(),
libzcash::JSInput()
};
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
libzcash::JSOutput(),
libzcash::JSOutput()
};
std::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
std::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
auto jsdesc = JSDescription::Randomized(
true,
*params, joinSplitPubKey, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false);
mtx.vjoinsplit.push_back(jsdesc);
}
mtx.vShieldedSpend.push_back(SpendDescription());
mtx.vjoinsplit[0].vpub_new = (MAX_MONEY / 2) + 10;
{
UNSAFE_CTransaction tx(mtx);
CValidationState state;
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
}
mtx.valueBalance = (MAX_MONEY / 2) + 10;
{
UNSAFE_CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txintotal-toolarge", false)).Times(1);
CheckTransactionWithoutProofVerification(tx, state);
}
}
// Test bad Overwinter version number in CheckTransactionWithoutProofVerification
TEST(checktransaction_tests, OverwinterVersionNumberLow) {
@@ -610,7 +807,7 @@ TEST(checktransaction_tests, OverwinterBadVersionGroupId) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0);
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nExpiryHeight = 0;
mtx.nVersionGroupId = 0x12345678;
@@ -626,13 +823,13 @@ TEST(checktransaction_tests, OverwinterNotActive) {
CMutableTransaction mtx = GetValidTransaction();
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
CTransaction tx(mtx);
MockCValidationState state;
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
EXPECT_CALL(state, DoS(0, false, REJECT_INVALID, "tx-overwinter-not-active", false)).Times(1);
ContextualCheckTransaction(tx, state, 1, 100);
}
@@ -643,7 +840,7 @@ TEST(checktransaction_tests, OverwinterFlagNotSet) {
CMutableTransaction mtx = GetValidTransaction();
mtx.fOverwintered = false;
mtx.nVersion = 3;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
@@ -684,7 +881,9 @@ TEST(checktransaction_tests, OverwinteredContextualCreateTx) {
SelectParams(CBaseChainParams::REGTEST);
const Consensus::Params& consensusParams = Params().GetConsensus();
int activationHeight = 5;
int saplingActivationHeight = 30;
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, activationHeight);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, saplingActivationHeight);
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
@@ -704,10 +903,64 @@ TEST(checktransaction_tests, OverwinteredContextualCreateTx) {
EXPECT_EQ(mtx.nVersion, 3);
EXPECT_EQ(mtx.fOverwintered, true);
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
EXPECT_EQ(mtx.nExpiryHeight, 0);
EXPECT_EQ(mtx.nExpiryHeight, activationHeight + expiryDelta);
}
// Close to Sapling activation
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
consensusParams, saplingActivationHeight - expiryDelta - 2);
EXPECT_EQ(mtx.fOverwintered, true);
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
EXPECT_EQ(mtx.nVersion, OVERWINTER_TX_VERSION);
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight - 2);
}
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
consensusParams, saplingActivationHeight - expiryDelta - 1);
EXPECT_EQ(mtx.fOverwintered, true);
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
EXPECT_EQ(mtx.nVersion, OVERWINTER_TX_VERSION);
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight - 1);
}
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
consensusParams, saplingActivationHeight - expiryDelta);
EXPECT_EQ(mtx.fOverwintered, true);
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
EXPECT_EQ(mtx.nVersion, OVERWINTER_TX_VERSION);
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight - 1);
}
// Just before Sapling activation
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
consensusParams, saplingActivationHeight - 1);
EXPECT_EQ(mtx.fOverwintered, true);
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
EXPECT_EQ(mtx.nVersion, OVERWINTER_TX_VERSION);
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight - 1);
}
// Sapling activates
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
consensusParams, saplingActivationHeight);
EXPECT_EQ(mtx.fOverwintered, true);
EXPECT_EQ(mtx.nVersionGroupId, SAPLING_VERSION_GROUP_ID);
EXPECT_EQ(mtx.nVersion, SAPLING_TX_VERSION);
EXPECT_EQ(mtx.nExpiryHeight, saplingActivationHeight + expiryDelta);
}
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
@@ -771,4 +1024,4 @@ TEST(checktransaction_tests, BadTxReceivedOverNetwork)
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception";
}
}
}
}

View File

@@ -106,7 +106,7 @@ bool test_merkle_gadget(
mgadget1.generate_r1cs_constraints();
mgadget2.generate_r1cs_constraints();
ZCIncrementalMerkleTree tree;
SproutMerkleTree tree;
uint256 commitment1_data = uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c");
uint256 commitment2_data = uint256S("59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7");
tree.append(commitment1_data);

View File

@@ -1,12 +1,16 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "chainparams.h"
#include "clientversion.h"
#include "deprecation.h"
#include "init.h"
#include "ui_interface.h"
#include "util.h"
#include "chainparams.h"
#include "utilstrencodings.h"
#include <boost/filesystem/operations.hpp>
#include <fstream>
using ::testing::StrictMock;
@@ -43,6 +47,18 @@ protected:
}
StrictMock<MockUIInterface> mock_;
static std::vector<std::string> read_lines(boost::filesystem::path filepath) {
std::vector<std::string> result;
std::ifstream f(filepath.string().c_str());
std::string line;
while (std::getline(f,line)) {
result.push_back(line);
}
return result;
}
};
TEST_F(DeprecationTest, NonDeprecatedNodeKeepsRunning) {
@@ -91,22 +107,6 @@ TEST_F(DeprecationTest, DeprecatedNodeErrorIsRepeatedOnStartup) {
EXPECT_TRUE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeShutsDownIfOldVersionDisabled) {
EXPECT_FALSE(ShutdownRequested());
mapArgs["-disabledeprecation"] = "1.0.0";
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
EXPECT_TRUE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeKeepsRunningIfCurrentVersionDisabled) {
EXPECT_FALSE(ShutdownRequested());
mapArgs["-disabledeprecation"] = CLIENT_VERSION_STR;
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
EXPECT_FALSE(ShutdownRequested());
}
TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnRegtest) {
SelectParams(CBaseChainParams::REGTEST);
EXPECT_FALSE(ShutdownRequested());
@@ -119,4 +119,31 @@ TEST_F(DeprecationTest, DeprecatedNodeIgnoredOnTestnet) {
EXPECT_FALSE(ShutdownRequested());
EnforceNodeDeprecation(DEPRECATION_HEIGHT+1);
EXPECT_FALSE(ShutdownRequested());
}
}
TEST_F(DeprecationTest, AlertNotify) {
boost::filesystem::path temp = GetTempPath() /
boost::filesystem::unique_path("alertnotify-%%%%.txt");
mapArgs["-alertnotify"] = std::string("echo %s >> ") + temp.string();
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING));
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT, false, false);
std::vector<std::string> r = read_lines(temp);
EXPECT_EQ(r.size(), 1u);
// -alertnotify restricts the message to safe characters.
auto expectedMsg = strprintf(
"This version will be deprecated at block height %d, and will automatically shut down. You should upgrade to the latest version of Zcash.",
DEPRECATION_HEIGHT);
// Windows built-in echo semantics are different than posixy shells. Quotes and
// whitespace are printed literally.
#ifndef WIN32
EXPECT_EQ(r[0], expectedMsg);
#else
EXPECT_EQ(r[0], strprintf("'%s' ", expectedMsg));
#endif
boost::filesystem::remove(temp);
}

View File

@@ -25,7 +25,6 @@
//
#if 0
TEST(founders_reward_test, create_testnet_2of3multisig) {
ECC_Start();
SelectParams(CBaseChainParams::TESTNET);
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
boost::filesystem::create_directories(pathTemp);
@@ -60,7 +59,7 @@ TEST(founders_reward_test, create_testnet_2of3multisig) {
pWallet->AddCScript(result);
pWallet->SetAddressBook(innerID, "", "receive");
std::string address = CBitcoinAddress(innerID).ToString();
std::string address = EncodeDestination(innerID);
addresses.push_back(address);
}
@@ -81,8 +80,6 @@ TEST(founders_reward_test, create_testnet_2of3multisig) {
std::cout << s << std::endl;
pWallet->Flush(true);
ECC_Stop();
}
#endif
@@ -107,11 +104,11 @@ TEST(founders_reward_test, general) {
// address = t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy
// script.ToString() = OP_HASH160 55d64928e69829d9376c776550b6cc710d427153 OP_EQUAL
// HexStr(script) = a91455d64928e69829d9376c776550b6cc710d42715387
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(1), ParseHex("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"));
EXPECT_EQ(HexStr(params.GetFoundersRewardScriptAtHeight(1)), "a914ef775f1f997f122a062fff1a2d7443abd1f9c64287");
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(1), "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi");
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(53126), ParseHex("a914ac67f4c072668138d88a86ff21b27207b283212f87"));
EXPECT_EQ(HexStr(params.GetFoundersRewardScriptAtHeight(53126)), "a914ac67f4c072668138d88a86ff21b27207b283212f87");
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(53126), "t2NGQjYMQhFndDHguvUw4wZdNdsssA6K7x2");
EXPECT_EQ(params.GetFoundersRewardScriptAtHeight(53127), ParseHex("a91455d64928e69829d9376c776550b6cc710d42715387"));
EXPECT_EQ(HexStr(params.GetFoundersRewardScriptAtHeight(53127)), "a91455d64928e69829d9376c776550b6cc710d42715387");
EXPECT_EQ(params.GetFoundersRewardAddressAtHeight(53127), "t2ENg7hHVqqs9JwU5cgjvSbxnT2a9USNfhy");
int maxHeight = params.GetConsensus().GetLastFoundersRewardBlockHeight();

View File

@@ -3,192 +3,222 @@
#include "utilstrencodings.h"
#include <boost/foreach.hpp>
#include <boost/variant/get.hpp>
#include "zcash/prf.h"
#include "util.h"
#include "streams.h"
#include "version.h"
#include "serialize.h"
#include "primitives/transaction.h"
#include "zcash/JoinSplit.hpp"
#include "zcash/Note.hpp"
#include "zcash/NoteEncryption.hpp"
#include "zcash/IncrementalMerkleTree.hpp"
#include <array>
using namespace libzcash;
extern ZCJoinSplit* params;
typedef std::array<JSDescription, 2> SproutProofs;
// Make both the PHGR and Groth proof for a Sprout statement,
// and store the results in JSDescription objects.
SproutProofs makeSproutProofs(
ZCJoinSplit& js,
const std::array<JSInput, 2>& inputs,
const std::array<JSOutput, 2>& outputs,
const uint256& joinSplitPubKey,
uint64_t vpub_old,
uint64_t vpub_new,
const uint256& rt
){
//Making the PHGR proof
JSDescription phgr(false, js, joinSplitPubKey, rt, inputs, outputs, vpub_old, vpub_new);
//Making the Groth proof
JSDescription groth(true, js, joinSplitPubKey, rt, inputs, outputs, vpub_old, vpub_new);
return {phgr, groth};
}
bool verifySproutProofs(
ZCJoinSplit& js,
const SproutProofs& jsdescs,
const uint256& joinSplitPubKey
)
{
auto verifier = libzcash::ProofVerifier::Strict();
bool phgrPassed = jsdescs[0].Verify(js, verifier, joinSplitPubKey);
bool grothPassed = jsdescs[1].Verify(js, verifier, joinSplitPubKey);
return phgrPassed && grothPassed;
}
void test_full_api(ZCJoinSplit* js)
{
// Create verification context.
auto verifier = libzcash::ProofVerifier::Strict();
// The recipient's information.
SpendingKey recipient_key = SpendingKey::random();
PaymentAddress recipient_addr = recipient_key.address();
SproutSpendingKey recipient_key = SproutSpendingKey::random();
SproutPaymentAddress recipient_addr = recipient_key.address();
// Create the commitment tree
ZCIncrementalMerkleTree tree;
SproutMerkleTree tree;
// Set up a JoinSplit description
uint256 ephemeralKey;
uint256 randomSeed;
uint64_t vpub_old = 10;
uint64_t vpub_new = 0;
uint256 pubKeyHash = random_uint256();
boost::array<uint256, 2> macs;
boost::array<uint256, 2> nullifiers;
boost::array<uint256, 2> commitments;
uint256 joinSplitPubKey = random_uint256();
uint256 rt = tree.root();
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
ZCProof proof;
SproutProofs jsdescs;
{
boost::array<JSInput, 2> inputs = {
std::array<JSInput, 2> inputs = {
JSInput(), // dummy input
JSInput() // dummy input
};
boost::array<JSOutput, 2> outputs = {
std::array<JSOutput, 2> outputs = {
JSOutput(recipient_addr, 10),
JSOutput() // dummy output
};
boost::array<Note, 2> output_notes;
std::array<SproutNote, 2> output_notes;
// Perform the proof
proof = js->prove(
// Perform the proofs
jsdescs = makeSproutProofs(
*js,
inputs,
outputs,
output_notes,
ciphertexts,
ephemeralKey,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
joinSplitPubKey,
vpub_old,
vpub_new,
rt
);
}
// Verify the transaction:
ASSERT_TRUE(js->verify(
proof,
verifier,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
));
// Recipient should decrypt
// Now the recipient should spend the money again
auto h_sig = js->h_sig(randomSeed, nullifiers, pubKeyHash);
ZCNoteDecryption decryptor(recipient_key.receiving_key());
auto note_pt = NotePlaintext::decrypt(
decryptor,
ciphertexts[0],
ephemeralKey,
h_sig,
0
);
auto decrypted_note = note_pt.note(recipient_addr);
ASSERT_TRUE(decrypted_note.value == 10);
// Insert the commitments from the last tx into the tree
tree.append(commitments[0]);
auto witness_recipient = tree.witness();
tree.append(commitments[1]);
witness_recipient.append(commitments[1]);
vpub_old = 0;
vpub_new = 1;
rt = tree.root();
pubKeyHash = random_uint256();
// Verify both PHGR and Groth Proof:
ASSERT_TRUE(verifySproutProofs(*js, jsdescs, joinSplitPubKey));
// Run tests using both phgr and groth as basis for field values
for (auto jsdesc : jsdescs)
{
boost::array<JSInput, 2> inputs = {
JSInput(), // dummy input
JSInput(witness_recipient, decrypted_note, recipient_key)
};
SproutMerkleTree tree;
SproutProofs jsdescs2;
// Recipient should decrypt
// Now the recipient should spend the money again
auto h_sig = js->h_sig(jsdesc.randomSeed, jsdesc.nullifiers, joinSplitPubKey);
ZCNoteDecryption decryptor(recipient_key.receiving_key());
SpendingKey second_recipient = SpendingKey::random();
PaymentAddress second_addr = second_recipient.address();
boost::array<JSOutput, 2> outputs = {
JSOutput(second_addr, 9),
JSOutput() // dummy output
};
boost::array<Note, 2> output_notes;
// Perform the proof
proof = js->prove(
inputs,
outputs,
output_notes,
ciphertexts,
ephemeralKey,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
auto note_pt = SproutNotePlaintext::decrypt(
decryptor,
jsdesc.ciphertexts[0],
jsdesc.ephemeralKey,
h_sig,
0
);
}
// Verify the transaction:
ASSERT_TRUE(js->verify(
proof,
verifier,
pubKeyHash,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt
));
auto decrypted_note = note_pt.note(recipient_addr);
ASSERT_TRUE(decrypted_note.value() == 10);
// Insert the commitments from the last tx into the tree
tree.append(jsdesc.commitments[0]);
auto witness_recipient = tree.witness();
tree.append(jsdesc.commitments[1]);
witness_recipient.append(jsdesc.commitments[1]);
vpub_old = 0;
vpub_new = 1;
rt = tree.root();
auto joinSplitPubKey2 = random_uint256();
{
std::array<JSInput, 2> inputs = {
JSInput(), // dummy input
JSInput(witness_recipient, decrypted_note, recipient_key)
};
SproutSpendingKey second_recipient = SproutSpendingKey::random();
SproutPaymentAddress second_addr = second_recipient.address();
std::array<JSOutput, 2> outputs = {
JSOutput(second_addr, 9),
JSOutput() // dummy output
};
std::array<SproutNote, 2> output_notes;
// Perform the proofs
jsdescs2 = makeSproutProofs(
*js,
inputs,
outputs,
joinSplitPubKey2,
vpub_old,
vpub_new,
rt
);
}
// Verify both PHGR and Groth Proof:
ASSERT_TRUE(verifySproutProofs(*js, jsdescs2, joinSplitPubKey2));
}
}
// Invokes the API (but does not compute a proof)
// to test exceptions
void invokeAPI(
ZCJoinSplit* js,
const boost::array<JSInput, 2>& inputs,
const boost::array<JSOutput, 2>& outputs,
const std::array<JSInput, 2>& inputs,
const std::array<JSOutput, 2>& outputs,
uint64_t vpub_old,
uint64_t vpub_new,
const uint256& rt
) {
uint256 ephemeralKey;
uint256 randomSeed;
uint256 pubKeyHash = random_uint256();
boost::array<uint256, 2> macs;
boost::array<uint256, 2> nullifiers;
boost::array<uint256, 2> commitments;
boost::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
uint256 joinSplitPubKey = random_uint256();
std::array<uint256, 2> macs;
std::array<uint256, 2> nullifiers;
std::array<uint256, 2> commitments;
std::array<ZCNoteEncryption::Ciphertext, 2> ciphertexts;
boost::array<Note, 2> output_notes;
std::array<SproutNote, 2> output_notes;
ZCProof proof = js->prove(
// PHGR
SproutProof proof = js->prove(
false,
inputs,
outputs,
output_notes,
ciphertexts,
ephemeralKey,
pubKeyHash,
joinSplitPubKey,
randomSeed,
macs,
nullifiers,
commitments,
vpub_old,
vpub_new,
rt,
false
);
// Groth
proof = js->prove(
true,
inputs,
outputs,
output_notes,
ciphertexts,
ephemeralKey,
joinSplitPubKey,
randomSeed,
macs,
nullifiers,
@@ -202,8 +232,8 @@ void invokeAPI(
void invokeAPIFailure(
ZCJoinSplit* js,
const boost::array<JSInput, 2>& inputs,
const boost::array<JSOutput, 2>& outputs,
const std::array<JSInput, 2>& inputs,
const std::array<JSOutput, 2>& outputs,
uint64_t vpub_old,
uint64_t vpub_new,
const uint256& rt,
@@ -228,9 +258,9 @@ TEST(joinsplit, h_sig)
import pyblake2
import binascii
def hSig(randomSeed, nf1, nf2, pubKeyHash):
def hSig(randomSeed, nf1, nf2, joinSplitPubKey):
return pyblake2.blake2b(
data=(randomSeed + nf1 + nf2 + pubKeyHash),
data=(randomSeed + nf1 + nf2 + joinSplitPubKey),
digest_size=32,
person=b"ZcashComputehSig"
).digest()
@@ -297,12 +327,12 @@ for test_input in TEST_VECTORS:
void increment_note_witnesses(
const uint256& element,
std::vector<ZCIncrementalWitness>& witnesses,
ZCIncrementalMerkleTree& tree
std::vector<SproutWitness>& witnesses,
SproutMerkleTree& tree
)
{
tree.append(element);
for (ZCIncrementalWitness& w : witnesses) {
for (SproutWitness& w : witnesses) {
w.append(element);
}
witnesses.push_back(tree.witness());
@@ -311,20 +341,20 @@ void increment_note_witnesses(
TEST(joinsplit, full_api_test)
{
{
std::vector<ZCIncrementalWitness> witnesses;
ZCIncrementalMerkleTree tree;
std::vector<SproutWitness> witnesses;
SproutMerkleTree tree;
increment_note_witnesses(uint256(), witnesses, tree);
SpendingKey sk = SpendingKey::random();
PaymentAddress addr = sk.address();
Note note1(addr.a_pk, 100, random_uint256(), random_uint256());
SproutSpendingKey sk = SproutSpendingKey::random();
SproutPaymentAddress addr = sk.address();
SproutNote note1(addr.a_pk, 100, random_uint256(), random_uint256());
increment_note_witnesses(note1.cm(), witnesses, tree);
Note note2(addr.a_pk, 100, random_uint256(), random_uint256());
SproutNote note2(addr.a_pk, 100, random_uint256(), random_uint256());
increment_note_witnesses(note2.cm(), witnesses, tree);
Note note3(addr.a_pk, 2100000000000001, random_uint256(), random_uint256());
SproutNote note3(addr.a_pk, 2100000000000001, random_uint256(), random_uint256());
increment_note_witnesses(note3.cm(), witnesses, tree);
Note note4(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
SproutNote note4(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
increment_note_witnesses(note4.cm(), witnesses, tree);
Note note5(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
SproutNote note5(addr.a_pk, 1900000000000000, random_uint256(), random_uint256());
increment_note_witnesses(note5.cm(), witnesses, tree);
// Should work
@@ -419,7 +449,7 @@ TEST(joinsplit, full_api_test)
// Wrong secret key
invokeAPIFailure(params,
{
JSInput(witnesses[1], note1, SpendingKey::random()),
JSInput(witnesses[1], note1, SproutSpendingKey::random()),
JSInput()
},
{
@@ -516,34 +546,71 @@ TEST(joinsplit, note_plaintexts)
uint256 a_pk = PRF_addr_a_pk(a_sk);
uint256 sk_enc = ZCNoteEncryption::generate_privkey(a_sk);
uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc);
PaymentAddress addr_pk(a_pk, pk_enc);
SproutPaymentAddress addr_pk(a_pk, pk_enc);
uint256 h_sig;
ZCNoteEncryption encryptor(h_sig);
uint256 epk = encryptor.get_epk();
Note note(a_pk,
SproutNote note(a_pk,
1945813,
random_uint256(),
random_uint256()
);
boost::array<unsigned char, ZC_MEMO_SIZE> memo;
std::array<unsigned char, ZC_MEMO_SIZE> memo;
NotePlaintext note_pt(note, memo);
SproutNotePlaintext note_pt(note, memo);
ZCNoteEncryption::Ciphertext ct = note_pt.encrypt(encryptor, pk_enc);
ZCNoteDecryption decryptor(sk_enc);
auto decrypted = NotePlaintext::decrypt(decryptor, ct, epk, h_sig, 0);
auto decrypted = SproutNotePlaintext::decrypt(decryptor, ct, epk, h_sig, 0);
auto decrypted_note = decrypted.note(addr_pk);
ASSERT_TRUE(decrypted_note.a_pk == note.a_pk);
ASSERT_TRUE(decrypted_note.rho == note.rho);
ASSERT_TRUE(decrypted_note.r == note.r);
ASSERT_TRUE(decrypted_note.value == note.value);
ASSERT_TRUE(decrypted_note.value() == note.value());
ASSERT_TRUE(decrypted.memo == note_pt.memo);
ASSERT_TRUE(decrypted.memo() == note_pt.memo());
// Check memo() returns by reference, not return by value, for use cases such as:
// std::string data(plaintext.memo().begin(), plaintext.memo().end());
ASSERT_TRUE(decrypted.memo().data() == decrypted.memo().data());
// Check serialization of note plaintext
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
ss << note_pt;
SproutNotePlaintext note_pt2;
ss >> note_pt2;
ASSERT_EQ(note_pt.value(), note.value());
ASSERT_EQ(note_pt.value(), note_pt2.value());
ASSERT_EQ(note_pt.memo(), note_pt2.memo());
ASSERT_EQ(note_pt.rho, note_pt2.rho);
ASSERT_EQ(note_pt.r, note_pt2.r);
}
TEST(joinsplit, note_class)
{
uint252 a_sk = uint252(uint256S("f6da8716682d600f74fc16bd0187faad6a26b4aa4c24d5c055b216d94516840e"));
uint256 a_pk = PRF_addr_a_pk(a_sk);
uint256 sk_enc = ZCNoteEncryption::generate_privkey(a_sk);
uint256 pk_enc = ZCNoteEncryption::generate_pubkey(sk_enc);
SproutPaymentAddress addr_pk(a_pk, pk_enc);
SproutNote note(a_pk,
1945813,
random_uint256(),
random_uint256());
SproutNote clone = note;
ASSERT_NE(&note, &clone);
ASSERT_EQ(note.value(), clone.value());
ASSERT_EQ(note.cm(), clone.cm());
ASSERT_EQ(note.rho, clone.rho);
ASSERT_EQ(note.r, clone.r);
ASSERT_EQ(note.a_pk, clone.a_pk);
}

47
src/gtest/test_keys.cpp Normal file
View File

@@ -0,0 +1,47 @@
#include <chainparams.h>
#include <key_io.h>
#include <zcash/Address.hpp>
#include <zcash/zip32.h>
#include <gtest/gtest.h>
TEST(Keys, DISABLED_EncodeAndDecodeSapling)
{
SelectParams(CBaseChainParams::MAIN);
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
for (uint32_t i = 0; i < 1000; i++) {
auto sk = m.Derive(i);
{
std::string sk_string = EncodeSpendingKey(sk);
EXPECT_EQ(
sk_string.substr(0, 24),
Params().Bech32HRP(CChainParams::SAPLING_EXTENDED_SPEND_KEY));
auto spendingkey2 = DecodeSpendingKey(sk_string);
EXPECT_TRUE(IsValidSpendingKey(spendingkey2));
ASSERT_TRUE(boost::get<libzcash::SaplingExtendedSpendingKey>(&spendingkey2) != nullptr);
auto sk2 = boost::get<libzcash::SaplingExtendedSpendingKey>(spendingkey2);
EXPECT_EQ(sk, sk2);
}
{
auto addr = sk.DefaultAddress();
std::string addr_string = EncodePaymentAddress(addr);
EXPECT_EQ(
addr_string.substr(0, 2),
Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS));
auto paymentaddr2 = DecodePaymentAddress(addr_string);
EXPECT_TRUE(IsValidPaymentAddress(paymentaddr2));
ASSERT_TRUE(boost::get<libzcash::SaplingPaymentAddress>(&paymentaddr2) != nullptr);
auto addr2 = boost::get<libzcash::SaplingPaymentAddress>(paymentaddr2);
EXPECT_EQ(addr, addr2);
}
}
}

View File

@@ -1,33 +1,121 @@
#include <gtest/gtest.h>
#include "test/data/sapling_key_components.json.h"
#include "keystore.h"
#include "random.h"
#ifdef ENABLE_WALLET
#include "wallet/crypter.h"
#endif
#include "zcash/Address.hpp"
#include "zcash/zip32.h"
#include "json_test_vectors.h"
#define MAKE_STRING(x) std::string((x), (x)+sizeof(x))
TEST(keystore_tests, StoreAndRetrieveHDSeed) {
CBasicKeyStore keyStore;
HDSeed seedOut;
// When we haven't set a seed, we shouldn't get one
EXPECT_FALSE(keyStore.HaveHDSeed());
EXPECT_FALSE(keyStore.GetHDSeed(seedOut));
// Generate a random seed
auto seed = HDSeed::Random();
// We should be able to set and retrieve the seed
ASSERT_TRUE(keyStore.SetHDSeed(seed));
EXPECT_TRUE(keyStore.HaveHDSeed());
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
EXPECT_EQ(seed, seedOut);
// Generate another random seed
auto seed2 = HDSeed::Random();
EXPECT_NE(seed, seed2);
// We should not be able to set and retrieve a different seed
EXPECT_FALSE(keyStore.SetHDSeed(seed2));
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
EXPECT_EQ(seed, seedOut);
}
TEST(keystore_tests, sapling_keys) {
// ["sk, ask, nsk, ovk, ak, nk, ivk, default_d, default_pk_d, note_v, note_r, note_cm, note_pos, note_nf"],
UniValue sapling_keys = read_json(MAKE_STRING(json_tests::sapling_key_components));
// Skipping over comments in sapling_key_components.json file
for (size_t i = 2; i < 12; i++) {
uint256 skSeed, ask, nsk, ovk, ak, nk, ivk;
skSeed.SetHex(sapling_keys[i][0].getValStr());
ask.SetHex(sapling_keys[i][1].getValStr());
nsk.SetHex(sapling_keys[i][2].getValStr());
ovk.SetHex(sapling_keys[i][3].getValStr());
ak.SetHex(sapling_keys[i][4].getValStr());
nk.SetHex(sapling_keys[i][5].getValStr());
ivk.SetHex(sapling_keys[i][6].getValStr());
libzcash::diversifier_t default_d;
std::copy_n(ParseHex(sapling_keys[i][7].getValStr()).begin(), 11, default_d.begin());
uint256 default_pk_d;
default_pk_d.SetHex(sapling_keys[i][8].getValStr());
auto sk = libzcash::SaplingSpendingKey(skSeed);
// Check that expanded spending key from primitives and from sk are the same
auto exp_sk_2 = libzcash::SaplingExpandedSpendingKey(ask, nsk, ovk);
auto exp_sk = sk.expanded_spending_key();
EXPECT_EQ(exp_sk, exp_sk_2);
// Check that full viewing key derived from sk and expanded sk are the same
auto full_viewing_key = sk.full_viewing_key();
EXPECT_EQ(full_viewing_key, exp_sk.full_viewing_key());
// Check that full viewing key from primitives and from sk are the same
auto full_viewing_key_2 = libzcash::SaplingFullViewingKey(ak, nk, ovk);
EXPECT_EQ(full_viewing_key, full_viewing_key_2);
// Check that incoming viewing key from primitives and from sk are the same
auto in_viewing_key = full_viewing_key.in_viewing_key();
auto in_viewing_key_2 = libzcash::SaplingIncomingViewingKey(ivk);
EXPECT_EQ(in_viewing_key, in_viewing_key_2);
// Check that the default address from primitives and from sk method are the same
auto default_addr = sk.default_address();
auto addrOpt2 = in_viewing_key.address(default_d);
EXPECT_TRUE(addrOpt2);
auto default_addr_2 = addrOpt2.value();
EXPECT_EQ(default_addr, default_addr_2);
auto default_addr_3 = libzcash::SaplingPaymentAddress(default_d, default_pk_d);
EXPECT_EQ(default_addr_2, default_addr_3);
EXPECT_EQ(default_addr, default_addr_3);
}
}
TEST(keystore_tests, store_and_retrieve_spending_key) {
CBasicKeyStore keyStore;
libzcash::SpendingKey skOut;
libzcash::SproutSpendingKey skOut;
std::set<libzcash::PaymentAddress> addrs;
keyStore.GetPaymentAddresses(addrs);
std::set<libzcash::SproutPaymentAddress> addrs;
keyStore.GetSproutPaymentAddresses(addrs);
EXPECT_EQ(0, addrs.size());
auto sk = libzcash::SpendingKey::random();
auto sk = libzcash::SproutSpendingKey::random();
auto addr = sk.address();
// Sanity-check: we can't get a key we haven't added
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut));
keyStore.AddSpendingKey(sk);
EXPECT_TRUE(keyStore.HaveSpendingKey(addr));
EXPECT_TRUE(keyStore.GetSpendingKey(addr, skOut));
keyStore.AddSproutSpendingKey(sk);
EXPECT_TRUE(keyStore.HaveSproutSpendingKey(addr));
EXPECT_TRUE(keyStore.GetSproutSpendingKey(addr, skOut));
EXPECT_EQ(sk, skOut);
keyStore.GetPaymentAddresses(addrs);
keyStore.GetSproutPaymentAddresses(addrs);
EXPECT_EQ(1, addrs.size());
EXPECT_EQ(1, addrs.count(addr));
}
@@ -36,48 +124,48 @@ TEST(keystore_tests, store_and_retrieve_note_decryptor) {
CBasicKeyStore keyStore;
ZCNoteDecryption decOut;
auto sk = libzcash::SpendingKey::random();
auto sk = libzcash::SproutSpendingKey::random();
auto addr = sk.address();
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
keyStore.AddSpendingKey(sk);
keyStore.AddSproutSpendingKey(sk);
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
}
TEST(keystore_tests, StoreAndRetrieveViewingKey) {
CBasicKeyStore keyStore;
libzcash::ViewingKey vkOut;
libzcash::SpendingKey skOut;
libzcash::SproutViewingKey vkOut;
libzcash::SproutSpendingKey skOut;
ZCNoteDecryption decOut;
auto sk = libzcash::SpendingKey::random();
auto sk = libzcash::SproutSpendingKey::random();
auto vk = sk.viewing_key();
auto addr = sk.address();
// Sanity-check: we can't get a viewing key we haven't added
EXPECT_FALSE(keyStore.HaveViewingKey(addr));
EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut));
EXPECT_FALSE(keyStore.HaveSproutViewingKey(addr));
EXPECT_FALSE(keyStore.GetSproutViewingKey(addr, vkOut));
// and we shouldn't have a spending key or decryptor either
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut));
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
// and we can't find it in our list of addresses
std::set<libzcash::PaymentAddress> addresses;
keyStore.GetPaymentAddresses(addresses);
std::set<libzcash::SproutPaymentAddress> addresses;
keyStore.GetSproutPaymentAddresses(addresses);
EXPECT_FALSE(addresses.count(addr));
keyStore.AddViewingKey(vk);
EXPECT_TRUE(keyStore.HaveViewingKey(addr));
EXPECT_TRUE(keyStore.GetViewingKey(addr, vkOut));
keyStore.AddSproutViewingKey(vk);
EXPECT_TRUE(keyStore.HaveSproutViewingKey(addr));
EXPECT_TRUE(keyStore.GetSproutViewingKey(addr, vkOut));
EXPECT_EQ(vk, vkOut);
// We should still not have the spending key...
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut));
// ... but we should have a decryptor
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
@@ -85,16 +173,16 @@ TEST(keystore_tests, StoreAndRetrieveViewingKey) {
// ... and we should find it in our list of addresses
addresses.clear();
keyStore.GetPaymentAddresses(addresses);
keyStore.GetSproutPaymentAddresses(addresses);
EXPECT_TRUE(addresses.count(addr));
keyStore.RemoveViewingKey(vk);
EXPECT_FALSE(keyStore.HaveViewingKey(addr));
EXPECT_FALSE(keyStore.GetViewingKey(addr, vkOut));
EXPECT_FALSE(keyStore.HaveSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSpendingKey(addr, skOut));
keyStore.RemoveSproutViewingKey(vk);
EXPECT_FALSE(keyStore.HaveSproutViewingKey(addr));
EXPECT_FALSE(keyStore.GetSproutViewingKey(addr, vkOut));
EXPECT_FALSE(keyStore.HaveSproutSpendingKey(addr));
EXPECT_FALSE(keyStore.GetSproutSpendingKey(addr, skOut));
addresses.clear();
keyStore.GetPaymentAddresses(addresses);
keyStore.GetSproutPaymentAddresses(addresses);
EXPECT_FALSE(addresses.count(addr));
// We still have a decryptor because those are cached in memory
@@ -103,6 +191,49 @@ TEST(keystore_tests, StoreAndRetrieveViewingKey) {
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
}
// Sapling
TEST(keystore_tests, StoreAndRetrieveSaplingSpendingKey) {
CBasicKeyStore keyStore;
libzcash::SaplingExtendedSpendingKey skOut;
libzcash::SaplingFullViewingKey fvkOut;
libzcash::SaplingIncomingViewingKey ivkOut;
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed(32);
HDSeed seed(rawSeed);
auto sk = libzcash::SaplingExtendedSpendingKey::Master(seed);
auto fvk = sk.expsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
auto addr = sk.DefaultAddress();
// Sanity-check: we can't get a key we haven't added
EXPECT_FALSE(keyStore.HaveSaplingSpendingKey(fvk));
EXPECT_FALSE(keyStore.GetSaplingSpendingKey(fvk, skOut));
// Sanity-check: we can't get a full viewing key we haven't added
EXPECT_FALSE(keyStore.HaveSaplingFullViewingKey(ivk));
EXPECT_FALSE(keyStore.GetSaplingFullViewingKey(ivk, fvkOut));
// Sanity-check: we can't get an incoming viewing key we haven't added
EXPECT_FALSE(keyStore.HaveSaplingIncomingViewingKey(addr));
EXPECT_FALSE(keyStore.GetSaplingIncomingViewingKey(addr, ivkOut));
// If we don't specify the default address, that mapping isn't created
keyStore.AddSaplingSpendingKey(sk);
EXPECT_TRUE(keyStore.HaveSaplingSpendingKey(fvk));
EXPECT_TRUE(keyStore.HaveSaplingFullViewingKey(ivk));
EXPECT_FALSE(keyStore.HaveSaplingIncomingViewingKey(addr));
// When we specify the default address, we get the full mapping
keyStore.AddSaplingSpendingKey(sk, addr);
EXPECT_TRUE(keyStore.HaveSaplingSpendingKey(fvk));
EXPECT_TRUE(keyStore.GetSaplingSpendingKey(fvk, skOut));
EXPECT_TRUE(keyStore.HaveSaplingFullViewingKey(ivk));
EXPECT_TRUE(keyStore.GetSaplingFullViewingKey(ivk, fvkOut));
EXPECT_TRUE(keyStore.HaveSaplingIncomingViewingKey(addr));
EXPECT_TRUE(keyStore.GetSaplingIncomingViewingKey(addr, ivkOut));
EXPECT_EQ(sk, skOut);
EXPECT_EQ(fvk, fvkOut);
EXPECT_EQ(ivk, ivkOut);
}
#ifdef ENABLE_WALLET
class TestCCryptoKeyStore : public CCryptoKeyStore
{
@@ -111,29 +242,89 @@ public:
bool Unlock(const CKeyingMaterial& vMasterKeyIn) { return CCryptoKeyStore::Unlock(vMasterKeyIn); }
};
TEST(keystore_tests, StoreAndRetrieveHDSeedInEncryptedStore) {
TestCCryptoKeyStore keyStore;
CKeyingMaterial vMasterKey(32, 0);
GetRandBytes(vMasterKey.data(), 32);
HDSeed seedOut;
// 1) Test adding a seed to an unencrypted key store, then encrypting it
auto seed = HDSeed::Random();
EXPECT_FALSE(keyStore.HaveHDSeed());
EXPECT_FALSE(keyStore.GetHDSeed(seedOut));
ASSERT_TRUE(keyStore.SetHDSeed(seed));
EXPECT_TRUE(keyStore.HaveHDSeed());
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
EXPECT_EQ(seed, seedOut);
ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey));
EXPECT_FALSE(keyStore.GetHDSeed(seedOut));
// Unlocking with a random key should fail
CKeyingMaterial vRandomKey(32, 0);
GetRandBytes(vRandomKey.data(), 32);
EXPECT_FALSE(keyStore.Unlock(vRandomKey));
// Unlocking with a slightly-modified vMasterKey should fail
CKeyingMaterial vModifiedKey(vMasterKey);
vModifiedKey[0] += 1;
EXPECT_FALSE(keyStore.Unlock(vModifiedKey));
// Unlocking with vMasterKey should succeed
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
EXPECT_EQ(seed, seedOut);
// 2) Test replacing the seed in an already-encrypted key store fails
auto seed2 = HDSeed::Random();
EXPECT_FALSE(keyStore.SetHDSeed(seed2));
EXPECT_TRUE(keyStore.HaveHDSeed());
ASSERT_TRUE(keyStore.GetHDSeed(seedOut));
EXPECT_EQ(seed, seedOut);
// 3) Test adding a new seed to an already-encrypted key store
TestCCryptoKeyStore keyStore2;
// Add a Sprout address so the wallet has something to test when decrypting
ASSERT_TRUE(keyStore2.AddSproutSpendingKey(libzcash::SproutSpendingKey::random()));
ASSERT_TRUE(keyStore2.EncryptKeys(vMasterKey));
ASSERT_TRUE(keyStore2.Unlock(vMasterKey));
EXPECT_FALSE(keyStore2.HaveHDSeed());
EXPECT_FALSE(keyStore2.GetHDSeed(seedOut));
auto seed3 = HDSeed::Random();
ASSERT_TRUE(keyStore2.SetHDSeed(seed3));
EXPECT_TRUE(keyStore2.HaveHDSeed());
ASSERT_TRUE(keyStore2.GetHDSeed(seedOut));
EXPECT_EQ(seed3, seedOut);
}
TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) {
TestCCryptoKeyStore keyStore;
uint256 r {GetRandHash()};
CKeyingMaterial vMasterKey (r.begin(), r.end());
libzcash::SpendingKey keyOut;
libzcash::SproutSpendingKey keyOut;
ZCNoteDecryption decOut;
std::set<libzcash::PaymentAddress> addrs;
std::set<libzcash::SproutPaymentAddress> addrs;
// 1) Test adding a key to an unencrypted key store, then encrypting it
auto sk = libzcash::SpendingKey::random();
auto sk = libzcash::SproutSpendingKey::random();
auto addr = sk.address();
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr, decOut));
keyStore.AddSpendingKey(sk);
ASSERT_TRUE(keyStore.HaveSpendingKey(addr));
ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut));
keyStore.AddSproutSpendingKey(sk);
ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr));
ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr, keyOut));
ASSERT_EQ(sk, keyOut);
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
ASSERT_TRUE(keyStore.EncryptKeys(vMasterKey));
ASSERT_TRUE(keyStore.HaveSpendingKey(addr));
ASSERT_FALSE(keyStore.GetSpendingKey(addr, keyOut));
ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr));
ASSERT_FALSE(keyStore.GetSproutSpendingKey(addr, keyOut));
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr, decOut));
EXPECT_EQ(ZCNoteDecryption(sk.receiving_key()), decOut);
@@ -149,38 +340,38 @@ TEST(keystore_tests, store_and_retrieve_spending_key_in_encrypted_store) {
// Unlocking with vMasterKey should succeed
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
ASSERT_TRUE(keyStore.GetSpendingKey(addr, keyOut));
ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr, keyOut));
ASSERT_EQ(sk, keyOut);
keyStore.GetPaymentAddresses(addrs);
keyStore.GetSproutPaymentAddresses(addrs);
ASSERT_EQ(1, addrs.size());
ASSERT_EQ(1, addrs.count(addr));
// 2) Test adding a spending key to an already-encrypted key store
auto sk2 = libzcash::SpendingKey::random();
auto sk2 = libzcash::SproutSpendingKey::random();
auto addr2 = sk2.address();
EXPECT_FALSE(keyStore.GetNoteDecryptor(addr2, decOut));
keyStore.AddSpendingKey(sk2);
ASSERT_TRUE(keyStore.HaveSpendingKey(addr2));
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
keyStore.AddSproutSpendingKey(sk2);
ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr2));
ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr2, keyOut));
ASSERT_EQ(sk2, keyOut);
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
ASSERT_TRUE(keyStore.Lock());
ASSERT_TRUE(keyStore.HaveSpendingKey(addr2));
ASSERT_FALSE(keyStore.GetSpendingKey(addr2, keyOut));
ASSERT_TRUE(keyStore.HaveSproutSpendingKey(addr2));
ASSERT_FALSE(keyStore.GetSproutSpendingKey(addr2, keyOut));
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
ASSERT_TRUE(keyStore.Unlock(vMasterKey));
ASSERT_TRUE(keyStore.GetSpendingKey(addr2, keyOut));
ASSERT_TRUE(keyStore.GetSproutSpendingKey(addr2, keyOut));
ASSERT_EQ(sk2, keyOut);
EXPECT_TRUE(keyStore.GetNoteDecryptor(addr2, decOut));
EXPECT_EQ(ZCNoteDecryption(sk2.receiving_key()), decOut);
keyStore.GetPaymentAddresses(addrs);
keyStore.GetSproutPaymentAddresses(addrs);
ASSERT_EQ(2, addrs.size());
ASSERT_EQ(1, addrs.count(addr));
ASSERT_EQ(1, addrs.count(addr2));

View File

@@ -19,11 +19,15 @@ class FakeCoinsViewDB : public CCoinsView {
public:
FakeCoinsViewDB() {}
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
return false;
}
bool GetNullifier(const uint256 &nf) const {
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
return false;
}
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
return false;
}
@@ -47,16 +51,19 @@ public:
return a;
}
uint256 GetBestAnchor() const {
uint256 GetBestAnchor(ShieldedType type) const {
uint256 a;
return a;
}
bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors,
CNullifiersMap &mapNullifiers) {
const uint256 &hashSproutAnchor,
const uint256 &hashSaplingAnchor,
CAnchorsSproutMap &mapSproutAnchors,
CAnchorsSaplingMap &mapSaplingAnchors,
CNullifiersMap &mapSproutNullifiers,
CNullifiersMap &mapSaplingNullifiers) {
return false;
}
@@ -96,7 +103,7 @@ TEST(Mempool, PriorityStatsDoNotCrash) {
}
TEST(Mempool, TxInputLimit) {
SelectParams(CBaseChainParams::TESTNET);
SelectParams(CBaseChainParams::REGTEST);
CTxMemPool pool(::minRelayTxFee);
bool missingInputs;
@@ -133,13 +140,30 @@ TEST(Mempool, TxInputLimit) {
// The -mempooltxinputlimit check doesn't set a reason
EXPECT_EQ(state3.GetRejectReason(), "");
// Clear the limit
mapArgs.erase("-mempooltxinputlimit");
// Activate Overwinter
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
// Check it no longer fails due to exceeding the limit
CValidationState state4;
EXPECT_FALSE(AcceptToMemoryPool(pool, state4, tx3, false, &missingInputs));
EXPECT_EQ(state4.GetRejectReason(), "bad-txns-version-too-low");
// Deactivate Overwinter
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
// Check it now fails due to exceeding the limit
CValidationState state5;
EXPECT_FALSE(AcceptToMemoryPool(pool, state5, tx3, false, &missingInputs));
// The -mempooltxinputlimit check doesn't set a reason
EXPECT_EQ(state5.GetRejectReason(), "");
// Clear the limit
mapArgs.erase("-mempooltxinputlimit");
// Check it no longer fails due to exceeding the limit
CValidationState state6;
EXPECT_FALSE(AcceptToMemoryPool(pool, state6, tx3, false, &missingInputs));
EXPECT_EQ(state6.GetRejectReason(), "bad-txns-version-too-low");
}
// Valid overwinter v3 format tx gets rejected because overwinter hasn't activated yet.
@@ -152,7 +176,7 @@ TEST(Mempool, OverwinterNotActiveYet) {
CMutableTransaction mtx = GetValidTransaction();
mtx.vjoinsplit.resize(0); // no joinsplits
mtx.fOverwintered = true;
mtx.nVersion = 3;
mtx.nVersion = OVERWINTER_TX_VERSION;
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nExpiryHeight = 0;
CValidationState state1;
@@ -224,7 +248,7 @@ TEST(Mempool, SproutNegativeVersionTxWhenOverwinterActive) {
mtx.vjoinsplit.resize(0); // no joinsplits
mtx.fOverwintered = false;
// A Sprout transaction with version -3 is created using Sprout code (as found in Zcashd <= 1.0.14).
// A Sprout transaction with version -3 is created using Sprout code (as found in zcashd <= 1.0.14).
// First four bytes of transaction, parsed as an uint32_t, has the value: 0xfffffffd
// This test simulates an Overwinter node receiving this transaction, but incorrectly deserializing the
// transaction due to a (pretend) bug of not detecting the most significant bit, which leads
@@ -242,7 +266,7 @@ TEST(Mempool, SproutNegativeVersionTxWhenOverwinterActive) {
EXPECT_EQ(state1.GetRejectReason(), "bad-txns-version-too-low");
}
// A Sprout transaction with version -3 created using Overwinter code (as found in Zcashd >= 1.0.15).
// A Sprout transaction with version -3 created using Overwinter code (as found in zcashd >= 1.0.15).
// First four bytes of transaction, parsed as an uint32_t, has the value: 0x80000003
// This test simulates the same pretend bug described above.
// The resulting Sprout tx with nVersion -2147483645 should be rejected by the Overwinter node's mempool.

View File

@@ -7,6 +7,13 @@
#include "test/data/merkle_path.json.h"
#include "test/data/merkle_commitments.json.h"
#include "test/data/merkle_roots_sapling.json.h"
#include "test/data/merkle_roots_empty_sapling.json.h"
#include "test/data/merkle_serialization_sapling.json.h"
#include "test/data/merkle_witness_serialization_sapling.json.h"
#include "test/data/merkle_path_sapling.json.h"
#include "test/data/merkle_commitments_sapling.json.h"
#include <iostream>
#include <stdexcept>
@@ -32,7 +39,7 @@ using namespace std;
using namespace libsnark;
template<>
void expect_deser_same(const ZCTestingIncrementalWitness& expected)
void expect_deser_same(const SproutTestingWitness& expected)
{
// Cannot check this; IncrementalWitness cannot be
// deserialized because it can only be constructed by
@@ -40,16 +47,6 @@ void expect_deser_same(const ZCTestingIncrementalWitness& expected)
// canonical serialized representation.
}
template<>
void expect_deser_same(const libzcash::MerklePath& expected)
{
// This deserialization check is pointless for MerklePath,
// since we only serialize it to check it against test
// vectors. See `expect_test_vector` for that. Also,
// it doesn't seem that vector<bool> can be properly
// deserialized by Bitcoin's serialization code.
}
template<typename A, typename B, typename C>
void expect_ser_test_vector(B& b, const C& c, const A& tree) {
expect_test_vector<B, C>(b, c);
@@ -61,7 +58,8 @@ void test_tree(
UniValue root_tests,
UniValue ser_tests,
UniValue witness_ser_tests,
UniValue path_tests
UniValue path_tests,
bool libsnark_test
)
{
size_t witness_ser_i = 0;
@@ -116,10 +114,9 @@ void test_tree(
ASSERT_THROW(wit.element(), std::runtime_error);
} else {
auto path = wit.path();
expect_test_vector(path_tests[path_i++], path);
{
expect_test_vector(path_tests[path_i++], path);
if (libsnark_test) {
typedef Fr<default_r1cs_ppzksnark_pp> FieldT;
protoboard<FieldT> pb;
@@ -146,7 +143,7 @@ void test_tree(
size_t path_index = convertVectorToInt(path.index);
commitment.bits.fill_with_bits(pb, bit_vector(commitment_bv));
positions.fill_with_bits_of_ulong(pb, path_index);
positions.fill_with_bits_of_uint64(pb, path_index);
authvars.generate_r1cs_witness(path_index, path.authentication_path);
auth.generate_r1cs_witness();
@@ -198,7 +195,31 @@ TEST(merkletree, vectors) {
UniValue path_tests = read_json(MAKE_STRING(json_tests::merkle_path));
UniValue commitment_tests = read_json(MAKE_STRING(json_tests::merkle_commitments));
test_tree<ZCTestingIncrementalMerkleTree, ZCTestingIncrementalWitness>(commitment_tests, root_tests, ser_tests, witness_ser_tests, path_tests);
test_tree<SproutTestingMerkleTree, SproutTestingWitness>(
commitment_tests,
root_tests,
ser_tests,
witness_ser_tests,
path_tests,
true
);
}
TEST(merkletree, SaplingVectors) {
UniValue root_tests = read_json(MAKE_STRING(json_tests::merkle_roots_sapling));
UniValue ser_tests = read_json(MAKE_STRING(json_tests::merkle_serialization_sapling));
UniValue witness_ser_tests = read_json(MAKE_STRING(json_tests::merkle_witness_serialization_sapling));
UniValue path_tests = read_json(MAKE_STRING(json_tests::merkle_path_sapling));
UniValue commitment_tests = read_json(MAKE_STRING(json_tests::merkle_commitments_sapling));
test_tree<SaplingTestingMerkleTree, SaplingTestingWitness>(
commitment_tests,
root_tests,
ser_tests,
witness_ser_tests,
path_tests,
false
);
}
TEST(merkletree, emptyroots) {
@@ -214,19 +235,41 @@ TEST(merkletree, emptyroots) {
ASSERT_TRUE(INCREMENTAL_MERKLE_TREE_DEPTH <= 64);
}
TEST(merkletree, EmptyrootsSapling) {
UniValue empty_roots = read_json(MAKE_STRING(json_tests::merkle_roots_empty_sapling));
libzcash::EmptyMerkleRoots<62, libzcash::PedersenHash> emptyroots;
for (size_t depth = 0; depth <= 62; depth++) {
expect_test_vector(empty_roots[depth], emptyroots.empty_root(depth));
}
// Double check that we're testing (at least) all the empty roots we'll use.
ASSERT_TRUE(INCREMENTAL_MERKLE_TREE_DEPTH <= 62);
}
TEST(merkletree, emptyroot) {
// This literal is the depth-20 empty tree root with the bytes reversed to
// account for the fact that uint256S() loads a big-endian representation of
// an integer which converted to little-endian internally.
uint256 expected = uint256S("59d2cde5e65c1414c32ba54f0fe4bdb3d67618125286e6a191317917c812c6d7");
ASSERT_TRUE(ZCIncrementalMerkleTree::empty_root() == expected);
ASSERT_TRUE(SproutMerkleTree::empty_root() == expected);
}
TEST(merkletree, EmptyrootSapling) {
// This literal is the depth-20 empty tree root with the bytes reversed to
// account for the fact that uint256S() loads a big-endian representation of
// an integer which converted to little-endian internally.
uint256 expected = uint256S("3e49b5f954aa9d3545bc6c37744661eea48d7c34e3000d82b7f0010c30f4c2fb");
ASSERT_TRUE(SaplingMerkleTree::empty_root() == expected);
}
TEST(merkletree, deserializeInvalid) {
// attempt to deserialize a small tree from a serialized large tree
// (exceeds depth well-formedness check)
ZCIncrementalMerkleTree newTree;
SproutMerkleTree newTree;
for (size_t i = 0; i < 16; i++) {
newTree.append(uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c"));
@@ -237,7 +280,7 @@ TEST(merkletree, deserializeInvalid) {
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << newTree;
ZCTestingIncrementalMerkleTree newTreeSmall;
SproutTestingMerkleTree newTreeSmall;
ASSERT_THROW({ss >> newTreeSmall;}, std::ios_base::failure);
}
@@ -249,7 +292,7 @@ TEST(merkletree, deserializeInvalid2) {
PROTOCOL_VERSION
);
ZCIncrementalMerkleTree tree;
SproutMerkleTree tree;
ASSERT_THROW(ss >> tree, std::ios_base::failure);
}
@@ -261,7 +304,7 @@ TEST(merkletree, deserializeInvalid3) {
PROTOCOL_VERSION
);
ZCIncrementalMerkleTree tree;
SproutMerkleTree tree;
ASSERT_THROW(ss >> tree, std::ios_base::failure);
}
@@ -273,15 +316,15 @@ TEST(merkletree, deserializeInvalid4) {
PROTOCOL_VERSION
);
ZCIncrementalMerkleTree tree;
SproutMerkleTree tree;
ASSERT_THROW(ss >> tree, std::ios_base::failure);
}
TEST(merkletree, testZeroElements) {
for (int start = 0; start < 20; start++) {
ZCIncrementalMerkleTree newTree;
SproutMerkleTree newTree;
ASSERT_TRUE(newTree.root() == ZCIncrementalMerkleTree::empty_root());
ASSERT_TRUE(newTree.root() == SproutMerkleTree::empty_root());
for (int i = start; i > 0; i--) {
newTree.append(uint256S("54d626e08c1c802b305dad30b7e54a82f102390cc92c7d4db112048935236e9c"));

View File

@@ -1,11 +1,15 @@
#include <gtest/gtest.h>
#include "sodium.h"
#include <array>
#include <stdexcept>
#include "zcash/Note.hpp"
#include "zcash/NoteEncryption.hpp"
#include "zcash/prf.h"
#include "zcash/Address.hpp"
#include "crypto/sha256.h"
#include "librustzcash.h"
class TestNoteDecryption : public ZCNoteDecryption {
public:
@@ -16,6 +20,327 @@ public:
}
};
TEST(noteencryption, NotePlaintext)
{
using namespace libzcash;
auto xsk = SaplingSpendingKey(uint256()).expanded_spending_key();
auto fvk = xsk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
SaplingPaymentAddress addr = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
std::array<unsigned char, ZC_MEMO_SIZE> memo;
for (size_t i = 0; i < ZC_MEMO_SIZE; i++) {
// Fill the message with dummy data
memo[i] = (unsigned char) i;
}
SaplingNote note(addr, 39393);
auto cmu_opt = note.cm();
if (!cmu_opt) {
FAIL();
}
uint256 cmu = cmu_opt.get();
SaplingNotePlaintext pt(note, memo);
auto res = pt.encrypt(addr.pk_d);
if (!res) {
FAIL();
}
auto enc = res.get();
auto ct = enc.first;
auto encryptor = enc.second;
auto epk = encryptor.get_epk();
// Try to decrypt with incorrect commitment
ASSERT_FALSE(SaplingNotePlaintext::decrypt(
ct,
ivk,
epk,
uint256()
));
// Try to decrypt with correct commitment
auto foo = SaplingNotePlaintext::decrypt(
ct,
ivk,
epk,
cmu
);
if (!foo) {
FAIL();
}
auto bar = foo.get();
ASSERT_TRUE(bar.value() == pt.value());
ASSERT_TRUE(bar.memo() == pt.memo());
ASSERT_TRUE(bar.d == pt.d);
ASSERT_TRUE(bar.rcm == pt.rcm);
auto foobar = bar.note(ivk);
if (!foobar) {
FAIL();
}
auto new_note = foobar.get();
ASSERT_TRUE(note.value() == new_note.value());
ASSERT_TRUE(note.d == new_note.d);
ASSERT_TRUE(note.pk_d == new_note.pk_d);
ASSERT_TRUE(note.r == new_note.r);
ASSERT_TRUE(note.cm() == new_note.cm());
SaplingOutgoingPlaintext out_pt;
out_pt.pk_d = note.pk_d;
out_pt.esk = encryptor.get_esk();
auto ovk = random_uint256();
auto cv = random_uint256();
auto cm = random_uint256();
auto out_ct = out_pt.encrypt(
ovk,
cv,
cm,
encryptor
);
auto decrypted_out_ct = out_pt.decrypt(
out_ct,
ovk,
cv,
cm,
encryptor.get_epk()
);
if (!decrypted_out_ct) {
FAIL();
}
auto decrypted_out_ct_unwrapped = decrypted_out_ct.get();
ASSERT_TRUE(decrypted_out_ct_unwrapped.pk_d == out_pt.pk_d);
ASSERT_TRUE(decrypted_out_ct_unwrapped.esk == out_pt.esk);
// Test sender won't accept invalid commitments
ASSERT_FALSE(
SaplingNotePlaintext::decrypt(
ct,
epk,
decrypted_out_ct_unwrapped.esk,
decrypted_out_ct_unwrapped.pk_d,
uint256()
)
);
// Test sender can decrypt the note ciphertext.
foo = SaplingNotePlaintext::decrypt(
ct,
epk,
decrypted_out_ct_unwrapped.esk,
decrypted_out_ct_unwrapped.pk_d,
cmu
);
if (!foo) {
FAIL();
}
bar = foo.get();
ASSERT_TRUE(bar.value() == pt.value());
ASSERT_TRUE(bar.memo() == pt.memo());
ASSERT_TRUE(bar.d == pt.d);
ASSERT_TRUE(bar.rcm == pt.rcm);
}
TEST(noteencryption, SaplingApi)
{
using namespace libzcash;
// Create recipient addresses
auto sk = SaplingSpendingKey(uint256()).expanded_spending_key();
auto vk = sk.full_viewing_key();
auto ivk = vk.in_viewing_key();
SaplingPaymentAddress pk_1 = *ivk.address({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
SaplingPaymentAddress pk_2 = *ivk.address({4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
// Blob of stuff we're encrypting
std::array<unsigned char, ZC_SAPLING_ENCPLAINTEXT_SIZE> message;
for (size_t i = 0; i < ZC_SAPLING_ENCPLAINTEXT_SIZE; i++) {
// Fill the message with dummy data
message[i] = (unsigned char) i;
}
std::array<unsigned char, ZC_SAPLING_OUTPLAINTEXT_SIZE> small_message;
for (size_t i = 0; i < ZC_SAPLING_OUTPLAINTEXT_SIZE; i++) {
// Fill the message with dummy data
small_message[i] = (unsigned char) i;
}
// Invalid diversifier
ASSERT_EQ(boost::none, SaplingNoteEncryption::FromDiversifier({1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
// Encrypt to pk_1
auto enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d);
auto ciphertext_1 = *enc.encrypt_to_recipient(
pk_1.pk_d,
message
);
auto epk_1 = enc.get_epk();
{
uint256 test_epk;
uint256 test_esk = enc.get_esk();
ASSERT_TRUE(librustzcash_sapling_ka_derivepublic(pk_1.d.begin(), test_esk.begin(), test_epk.begin()));
ASSERT_TRUE(test_epk == epk_1);
}
auto cv_1 = random_uint256();
auto cm_1 = random_uint256();
auto out_ciphertext_1 = enc.encrypt_to_ourselves(
sk.ovk,
cv_1,
cm_1,
small_message
);
// Encrypt to pk_2
enc = *SaplingNoteEncryption::FromDiversifier(pk_2.d);
auto ciphertext_2 = *enc.encrypt_to_recipient(
pk_2.pk_d,
message
);
auto epk_2 = enc.get_epk();
auto cv_2 = random_uint256();
auto cm_2 = random_uint256();
auto out_ciphertext_2 = enc.encrypt_to_ourselves(
sk.ovk,
cv_2,
cm_2,
small_message
);
// Test nonce-reuse resistance of API
{
auto tmp_enc = *SaplingNoteEncryption::FromDiversifier(pk_1.d);
tmp_enc.encrypt_to_recipient(
pk_1.pk_d,
message
);
ASSERT_THROW(tmp_enc.encrypt_to_recipient(
pk_1.pk_d,
message
), std::logic_error);
tmp_enc.encrypt_to_ourselves(
sk.ovk,
cv_2,
cm_2,
small_message
);
ASSERT_THROW(tmp_enc.encrypt_to_ourselves(
sk.ovk,
cv_2,
cm_2,
small_message
), std::logic_error);
}
// Try to decrypt
auto plaintext_1 = *AttemptSaplingEncDecryption(
ciphertext_1,
ivk,
epk_1
);
ASSERT_TRUE(message == plaintext_1);
auto small_plaintext_1 = *AttemptSaplingOutDecryption(
out_ciphertext_1,
sk.ovk,
cv_1,
cm_1,
epk_1
);
ASSERT_TRUE(small_message == small_plaintext_1);
auto plaintext_2 = *AttemptSaplingEncDecryption(
ciphertext_2,
ivk,
epk_2
);
ASSERT_TRUE(message == plaintext_2);
auto small_plaintext_2 = *AttemptSaplingOutDecryption(
out_ciphertext_2,
sk.ovk,
cv_2,
cm_2,
epk_2
);
ASSERT_TRUE(small_message == small_plaintext_2);
// Try to decrypt out ciphertext with wrong key material
ASSERT_FALSE(AttemptSaplingOutDecryption(
out_ciphertext_1,
random_uint256(),
cv_1,
cm_1,
epk_1
));
ASSERT_FALSE(AttemptSaplingOutDecryption(
out_ciphertext_1,
sk.ovk,
random_uint256(),
cm_1,
epk_1
));
ASSERT_FALSE(AttemptSaplingOutDecryption(
out_ciphertext_1,
sk.ovk,
cv_1,
random_uint256(),
epk_1
));
ASSERT_FALSE(AttemptSaplingOutDecryption(
out_ciphertext_1,
sk.ovk,
cv_1,
cm_1,
random_uint256()
));
// Try to decrypt with wrong ephemeral key
ASSERT_FALSE(AttemptSaplingEncDecryption(
ciphertext_1,
ivk,
epk_2
));
ASSERT_FALSE(AttemptSaplingEncDecryption(
ciphertext_2,
ivk,
epk_1
));
// Try to decrypt with wrong ivk
ASSERT_FALSE(AttemptSaplingEncDecryption(
ciphertext_1,
uint256(),
epk_1
));
ASSERT_FALSE(AttemptSaplingEncDecryption(
ciphertext_2,
uint256(),
epk_2
));
}
TEST(noteencryption, api)
{
uint256 sk_enc = ZCNoteEncryption::generate_privkey(uint252(uint256S("21035d60bc1983e37950ce4803418a8fb33ea68d5b937ca382ecbae7564d6a07")));
@@ -29,7 +354,7 @@ TEST(noteencryption, api)
ASSERT_TRUE(b.get_epk() != c.get_epk());
}
boost::array<unsigned char, ZC_NOTEPLAINTEXT_SIZE> message;
std::array<unsigned char, ZC_NOTEPLAINTEXT_SIZE> message;
for (size_t i = 0; i < ZC_NOTEPLAINTEXT_SIZE; i++) {
// Fill the message with dummy data
message[i] = (unsigned char) i;

View File

@@ -7,6 +7,8 @@
#include "zcash/Address.hpp"
#include "wallet/wallet.h"
#include "amount.h"
#include <array>
#include <memory>
#include <string>
#include <set>
@@ -91,14 +93,13 @@ public:
// Note that the zpd: prefix is not part of the payment disclosure blob itself. It is only
// used as convention to improve the user experience when sharing payment disclosure blobs.
TEST(paymentdisclosure, mainnet) {
ECC_Start();
SelectParams(CBaseChainParams::MAIN);
boost::filesystem::path pathTemp = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
boost::filesystem::create_directories(pathTemp);
mapArgs["-datadir"] = pathTemp.string();
std::cout << "Test payment disclosure database created in folder: " << pathTemp.native() << std::endl;
std::cout << "Test payment disclosure database created in folder: " << pathTemp.string() << std::endl;
PaymentDisclosureDBTest mydb(pathTemp);
@@ -120,7 +121,7 @@ TEST(paymentdisclosure, mainnet) {
PaymentDisclosureInfo info;
info.esk = random_uint256();
info.joinSplitPrivKey = joinSplitPrivKey;
info.zaddr = libzcash::SpendingKey::random().address();
info.zaddr = libzcash::SproutSpendingKey::random().address();
ASSERT_TRUE(mydb.Put(key, info));
// Retrieve info from test database into new local variable and test it matches
@@ -131,7 +132,7 @@ TEST(paymentdisclosure, mainnet) {
// Modify this local variable and confirm it no longer matches
info2.esk = random_uint256();
info2.joinSplitPrivKey = random_uint256();
info2.zaddr = libzcash::SpendingKey::random().address();
info2.zaddr = libzcash::SproutSpendingKey::random().address();
ASSERT_NE(info, info2);
// Using the payment info object, let's create a dummy payload
@@ -167,7 +168,7 @@ TEST(paymentdisclosure, mainnet) {
}
// Convert signature buffer to boost array
boost::array<unsigned char, 64> arrayPayloadSig;
std::array<unsigned char, 64> arrayPayloadSig;
memcpy(arrayPayloadSig.data(), &payloadSig[0], 64);
// Payment disclosure blob to pass around
@@ -207,6 +208,4 @@ TEST(paymentdisclosure, mainnet) {
#if DUMP_DATABASE_TO_STDOUT == true
mydb.DebugDumpAllStdout();
#endif
ECC_Stop();
}

View File

@@ -0,0 +1,15 @@
#include <gtest/gtest.h>
#include "librustzcash.h"
#include "uint256.h"
TEST(PedersenHash, TestAPI) {
const uint256 a = uint256S("87a086ae7d2252d58729b30263fb7b66308bf94ef59a76c9c86e7ea016536505");
const uint256 b = uint256S("a75b84a125b2353da7e8d96ee2a15efe4de23df9601b9d9564ba59de57130406");
uint256 result;
librustzcash_merkle_hash(25, a.begin(), b.begin(), result.begin());
uint256 expected_result = uint256S("5bf43b5736c19b714d1f462c9d22ba3492c36e3d9bbd7ca24d94b440550aa561");
ASSERT_TRUE(result == expected_result);
}

View File

@@ -241,7 +241,7 @@ TEST(proofs, sqrt_fq2)
TEST(proofs, size_is_expected)
{
ZCProof p;
PHGRProof p;
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << p;
@@ -444,7 +444,7 @@ TEST(proofs, zksnark_serializes_properly)
auto vkprecomp = libsnark::r1cs_ppzksnark_verifier_process_vk(kp.vk);
for (size_t i = 0; i < 20; i++) {
auto badproof = ZCProof::random_invalid();
auto badproof = PHGRProof::random_invalid();
auto proof = badproof.to_libsnark_proof<libsnark::r1cs_ppzksnark_proof<curve_pp>>();
auto verifierEnabled = ProofVerifier::Strict();
@@ -496,12 +496,12 @@ TEST(proofs, zksnark_serializes_properly)
proof
));
ZCProof compressed_proof_0(proof);
PHGRProof compressed_proof_0(proof);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << compressed_proof_0;
ZCProof compressed_proof_1;
PHGRProof compressed_proof_1;
ss >> compressed_proof_1;
ASSERT_TRUE(compressed_proof_0 == compressed_proof_1);

View File

@@ -5,7 +5,7 @@
#include "chainparams.h"
#include "clientversion.h"
#include "primitives/block.h"
#include "rpcserver.h"
#include "rpc/server.h"
#include "streams.h"
#include "utilstrencodings.h"

View File

@@ -0,0 +1,72 @@
#include <gtest/gtest.h>
#include "zcash/Address.hpp"
#include "zcash/Note.hpp"
#include "amount.h"
#include "random.h"
#include "librustzcash.h"
#include <array>
using namespace libzcash;
// Test data from https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_key_components.py
TEST(SaplingNote, TestVectors)
{
uint64_t v = 0;
uint64_t note_pos = 0;
std::array<uint8_t, 11> diversifier{0xf1, 0x9d, 0x9b, 0x79, 0x7e, 0x39, 0xf3, 0x37, 0x44, 0x58, 0x39};
std::vector<uint8_t> v_sk{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
std::vector<uint8_t> v_pk_d{
0xdb, 0x4c, 0xd2, 0xb0, 0xaa, 0xc4, 0xf7, 0xeb, 0x8c, 0xa1, 0x31, 0xf1, 0x65, 0x67,
0xc4, 0x45, 0xa9, 0x55, 0x51, 0x26, 0xd3, 0xc2, 0x9f, 0x14, 0xe3, 0xd7, 0x76, 0xe8,
0x41, 0xae, 0x74, 0x15};
std::vector<uint8_t> v_r{
0x39, 0x17, 0x6d, 0xac, 0x39, 0xac, 0xe4, 0x98, 0x0e, 0xcc, 0x8d, 0x77, 0x8e, 0x89,
0x86, 0x02, 0x55, 0xec, 0x36, 0x15, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
std::vector<uint8_t> v_cm{
0xcb, 0x3c, 0xf9, 0x15, 0x32, 0x70, 0xd5, 0x7e, 0xb9, 0x14, 0xc6, 0xc2, 0xbc, 0xc0,
0x18, 0x50, 0xc9, 0xfe, 0xd4, 0x4f, 0xce, 0x08, 0x06, 0x27, 0x8f, 0x08, 0x3e, 0xf2,
0xdd, 0x07, 0x64, 0x39};
std::vector<uint8_t> v_nf{
0x44, 0xfa, 0xd6, 0x56, 0x4f, 0xfd, 0xec, 0x9f, 0xa1, 0x9c, 0x43, 0xa2, 0x8f, 0x86,
0x1d, 0x5e, 0xbf, 0x60, 0x23, 0x46, 0x00, 0x7d, 0xe7, 0x62, 0x67, 0xd9, 0x75, 0x27,
0x47, 0xab, 0x40, 0x63};
uint256 sk(v_sk);
uint256 pk_d(v_pk_d);
uint256 r(v_r);
uint256 cm(v_cm);
uint256 nf(v_nf);
// Test commitment
SaplingNote note = SaplingNote(diversifier, pk_d, v, r);
ASSERT_EQ(note.cm().get(), cm);
// Test nullifier
SaplingSpendingKey spendingKey(sk);
ASSERT_EQ(note.nullifier(spendingKey.full_viewing_key(), note_pos), nf);
}
TEST(SaplingNote, Random)
{
// Test creating random notes using the same spending key
auto address = SaplingSpendingKey::random().default_address();
SaplingNote note1(address, GetRand(MAX_MONEY));
SaplingNote note2(address, GetRand(MAX_MONEY));
ASSERT_EQ(note1.d, note2.d);
ASSERT_EQ(note1.pk_d, note2.pk_d);
ASSERT_NE(note1.value(), note2.value());
ASSERT_NE(note1.r, note2.r);
// Test diversifier and pk_d are not the same for different spending keys
SaplingNote note3(SaplingSpendingKey::random().default_address(), GetRand(MAX_MONEY));
ASSERT_NE(note1.d, note3.d);
ASSERT_NE(note1.pk_d, note3.pk_d);
}

View File

@@ -4,18 +4,20 @@
#include "zcash/Note.hpp"
#include "zcash/Address.hpp"
#include <array>
extern ZCJoinSplit* params;
extern int GenZero(int n);
extern int GenMax(int n);
TEST(Transaction, JSDescriptionRandomized) {
// construct a merkle tree
ZCIncrementalMerkleTree merkleTree;
SproutMerkleTree merkleTree;
libzcash::SpendingKey k = libzcash::SpendingKey::random();
libzcash::PaymentAddress addr = k.address();
libzcash::SproutSpendingKey k = libzcash::SproutSpendingKey::random();
libzcash::SproutPaymentAddress addr = k.address();
libzcash::Note note(addr.a_pk, 100, uint256(), uint256());
libzcash::SproutNote note(addr.a_pk, 100, uint256(), uint256());
// commitment from coin
uint256 commitment = note.cm();
@@ -29,81 +31,59 @@ TEST(Transaction, JSDescriptionRandomized) {
auto witness = merkleTree.witness();
// create JSDescription
uint256 pubKeyHash;
boost::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
uint256 joinSplitPubKey;
std::array<libzcash::JSInput, ZC_NUM_JS_INPUTS> inputs = {
libzcash::JSInput(witness, note, k),
libzcash::JSInput() // dummy input of zero value
};
boost::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
std::array<libzcash::JSOutput, ZC_NUM_JS_OUTPUTS> outputs = {
libzcash::JSOutput(addr, 50),
libzcash::JSOutput(addr, 50)
};
#ifdef __LP64__ // required for building on MacOS
boost::array<uint64_t, ZC_NUM_JS_INPUTS> inputMap;
boost::array<uint64_t, ZC_NUM_JS_OUTPUTS> outputMap;
#else
boost::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
boost::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
#endif
std::array<size_t, ZC_NUM_JS_INPUTS> inputMap;
std::array<size_t, ZC_NUM_JS_OUTPUTS> outputMap;
{
auto jsdesc = JSDescription::Randomized(
*params, pubKeyHash, rt,
false,
*params, joinSplitPubKey, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false);
#ifdef __LP64__ // required for building on MacOS
std::set<uint64_t> inputSet(inputMap.begin(), inputMap.end());
std::set<uint64_t> expectedInputSet {0, 1};
#else
std::set<size_t> inputSet(inputMap.begin(), inputMap.end());
std::set<size_t> expectedInputSet {0, 1};
#endif
EXPECT_EQ(expectedInputSet, inputSet);
#ifdef __LP64__ // required for building on MacOS
std::set<uint64_t> outputSet(outputMap.begin(), outputMap.end());
std::set<uint64_t> expectedOutputSet {0, 1};
#else
std::set<size_t> outputSet(outputMap.begin(), outputMap.end());
std::set<size_t> expectedOutputSet {0, 1};
#endif
EXPECT_EQ(expectedOutputSet, outputSet);
}
{
auto jsdesc = JSDescription::Randomized(
*params, pubKeyHash, rt,
false,
*params, joinSplitPubKey, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false, nullptr, GenZero);
#ifdef __LP64__ // required for building on MacOS
boost::array<uint64_t, ZC_NUM_JS_INPUTS> expectedInputMap {1, 0};
boost::array<uint64_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {1, 0};
#else
boost::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {1, 0};
boost::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {1, 0};
#endif
std::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {1, 0};
std::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {1, 0};
EXPECT_EQ(expectedInputMap, inputMap);
EXPECT_EQ(expectedOutputMap, outputMap);
}
{
auto jsdesc = JSDescription::Randomized(
*params, pubKeyHash, rt,
false,
*params, joinSplitPubKey, rt,
inputs, outputs,
inputMap, outputMap,
0, 0, false, nullptr, GenMax);
#ifdef __LP64__ // required for building on MacOS
boost::array<uint64_t, ZC_NUM_JS_INPUTS> expectedInputMap {0, 1};
boost::array<uint64_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {0, 1};
#else
boost::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {0, 1};
boost::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {0, 1};
#endif
std::array<size_t, ZC_NUM_JS_INPUTS> expectedInputMap {0, 1};
std::array<size_t, ZC_NUM_JS_OUTPUTS> expectedOutputMap {0, 1};
EXPECT_EQ(expectedInputMap, inputMap);
EXPECT_EQ(expectedOutputMap, outputMap);
}

View File

@@ -0,0 +1,335 @@
#include "chainparams.h"
#include "consensus/params.h"
#include "consensus/validation.h"
#include "key_io.h"
#include "main.h"
#include "pubkey.h"
#include "transaction_builder.h"
#include "zcash/Address.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
static const std::string tSecretRegtest = "cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN";
TEST(TransactionBuilder, Invoke)
{
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto consensusParams = Params().GetConsensus();
CBasicKeyStore keystore;
CKey tsk = DecodeSecret(tSecretRegtest);
keystore.AddKey(tsk);
auto scriptPubKey = GetScriptForDestination(tsk.GetPubKey().GetID());
auto sk_from = libzcash::SaplingSpendingKey::random();
auto fvk_from = sk_from.full_viewing_key();
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
auto ivk = fvk.in_viewing_key();
libzcash::diversifier_t d = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
auto pk = *ivk.address(d);
// Create a shielding transaction from transparent to Sapling
// 0.0005 t-ZEC in, 0.0004 z-ZEC out, 0.0001 t-ZEC fee
auto builder1 = TransactionBuilder(consensusParams, 1, &keystore);
builder1.AddTransparentInput(COutPoint(), scriptPubKey, 50000);
builder1.AddSaplingOutput(fvk_from, pk, 40000, {});
auto maybe_tx1 = builder1.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx1), true);
auto tx1 = maybe_tx1.get();
EXPECT_EQ(tx1.vin.size(), 1);
EXPECT_EQ(tx1.vout.size(), 0);
EXPECT_EQ(tx1.vjoinsplit.size(), 0);
EXPECT_EQ(tx1.vShieldedSpend.size(), 0);
EXPECT_EQ(tx1.vShieldedOutput.size(), 1);
EXPECT_EQ(tx1.valueBalance, -40000);
CValidationState state;
EXPECT_TRUE(ContextualCheckTransaction(tx1, state, 2, 0));
EXPECT_EQ(state.GetRejectReason(), "");
// Prepare to spend the note that was just created
auto maybe_pt = libzcash::SaplingNotePlaintext::decrypt(
tx1.vShieldedOutput[0].encCiphertext, ivk, tx1.vShieldedOutput[0].ephemeralKey, tx1.vShieldedOutput[0].cm);
ASSERT_EQ(static_cast<bool>(maybe_pt), true);
auto maybe_note = maybe_pt.get().note(ivk);
ASSERT_EQ(static_cast<bool>(maybe_note), true);
auto note = maybe_note.get();
SaplingMerkleTree tree;
tree.append(tx1.vShieldedOutput[0].cm);
auto anchor = tree.root();
auto witness = tree.witness();
// Create a Sapling-only transaction
// 0.0004 z-ZEC in, 0.00025 z-ZEC out, 0.0001 t-ZEC fee, 0.00005 z-ZEC change
auto builder2 = TransactionBuilder(consensusParams, 2);
ASSERT_TRUE(builder2.AddSaplingSpend(expsk, note, anchor, witness));
// Check that trying to add a different anchor fails
ASSERT_FALSE(builder2.AddSaplingSpend(expsk, note, uint256(), witness));
builder2.AddSaplingOutput(fvk, pk, 25000, {});
auto maybe_tx2 = builder2.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx2), true);
auto tx2 = maybe_tx2.get();
EXPECT_EQ(tx2.vin.size(), 0);
EXPECT_EQ(tx2.vout.size(), 0);
EXPECT_EQ(tx2.vjoinsplit.size(), 0);
EXPECT_EQ(tx2.vShieldedSpend.size(), 1);
EXPECT_EQ(tx2.vShieldedOutput.size(), 2);
EXPECT_EQ(tx2.valueBalance, 10000);
EXPECT_TRUE(ContextualCheckTransaction(tx2, state, 3, 0));
EXPECT_EQ(state.GetRejectReason(), "");
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(TransactionBuilder, ThrowsOnTransparentInputWithoutKeyStore)
{
auto consensusParams = Params().GetConsensus();
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_THROW(builder.AddTransparentInput(COutPoint(), CScript(), 1), std::runtime_error);
}
TEST(TransactionBuilder, RejectsInvalidTransparentOutput)
{
auto consensusParams = Params().GetConsensus();
// Default CTxDestination type is an invalid address
CTxDestination taddr;
auto builder = TransactionBuilder(consensusParams, 1);
EXPECT_FALSE(builder.AddTransparentOutput(taddr, 50));
}
TEST(TransactionBuilder, RejectsInvalidTransparentChangeAddress)
{
auto consensusParams = Params().GetConsensus();
// Default CTxDestination type is an invalid address
CTxDestination taddr;
auto builder = TransactionBuilder(consensusParams, 1);
EXPECT_FALSE(builder.SendChangeTo(taddr));
}
TEST(TransactionBuilder, FailsWithNegativeChange)
{
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto consensusParams = Params().GetConsensus();
// Generate dummy Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
auto pk = sk.default_address();
// Set up dummy transparent address
CBasicKeyStore keystore;
CKey tsk = DecodeSecret(tSecretRegtest);
keystore.AddKey(tsk);
auto tkeyid = tsk.GetPubKey().GetID();
auto scriptPubKey = GetScriptForDestination(tkeyid);
CTxDestination taddr = tkeyid;
// Generate dummy Sapling note
libzcash::SaplingNote note(pk, 59999);
auto cm = note.cm().value();
SaplingMerkleTree tree;
tree.append(cm);
auto anchor = tree.root();
auto witness = tree.witness();
// Fail if there is only a Sapling output
// 0.0005 z-ZEC out, 0.0001 t-ZEC fee
auto builder = TransactionBuilder(consensusParams, 1);
builder.AddSaplingOutput(fvk, pk, 50000, {});
EXPECT_FALSE(static_cast<bool>(builder.Build()));
// Fail if there is only a transparent output
// 0.0005 t-ZEC out, 0.0001 t-ZEC fee
builder = TransactionBuilder(consensusParams, 1, &keystore);
EXPECT_TRUE(builder.AddTransparentOutput(taddr, 50000));
EXPECT_FALSE(static_cast<bool>(builder.Build()));
// Fails if there is insufficient input
// 0.0005 t-ZEC out, 0.0001 t-ZEC fee, 0.00059999 z-ZEC in
EXPECT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
EXPECT_FALSE(static_cast<bool>(builder.Build()));
// Succeeds if there is sufficient input
builder.AddTransparentInput(COutPoint(), scriptPubKey, 1);
EXPECT_TRUE(static_cast<bool>(builder.Build()));
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(TransactionBuilder, ChangeOutput)
{
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto consensusParams = Params().GetConsensus();
// Generate dummy Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto pk = sk.default_address();
// Generate dummy Sapling note
libzcash::SaplingNote note(pk, 25000);
auto cm = note.cm().value();
SaplingMerkleTree tree;
tree.append(cm);
auto anchor = tree.root();
auto witness = tree.witness();
// Generate change Sapling address
auto sk2 = libzcash::SaplingSpendingKey::random();
auto fvkOut = sk2.full_viewing_key();
auto zChangeAddr = sk2.default_address();
// Set up dummy transparent address
CBasicKeyStore keystore;
CKey tsk = DecodeSecret(tSecretRegtest);
keystore.AddKey(tsk);
auto tkeyid = tsk.GetPubKey().GetID();
auto scriptPubKey = GetScriptForDestination(tkeyid);
CTxDestination taddr = tkeyid;
// No change address and no Sapling spends
{
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
EXPECT_FALSE(static_cast<bool>(builder.Build()));
}
// Change to the same address as the first Sapling spend
{
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
EXPECT_EQ(tx.vin.size(), 1);
EXPECT_EQ(tx.vout.size(), 0);
EXPECT_EQ(tx.vjoinsplit.size(), 0);
EXPECT_EQ(tx.vShieldedSpend.size(), 1);
EXPECT_EQ(tx.vShieldedOutput.size(), 1);
EXPECT_EQ(tx.valueBalance, -15000);
}
// Change to a Sapling address
{
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
builder.SendChangeTo(zChangeAddr, fvkOut);
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
EXPECT_EQ(tx.vin.size(), 1);
EXPECT_EQ(tx.vout.size(), 0);
EXPECT_EQ(tx.vjoinsplit.size(), 0);
EXPECT_EQ(tx.vShieldedSpend.size(), 0);
EXPECT_EQ(tx.vShieldedOutput.size(), 1);
EXPECT_EQ(tx.valueBalance, -15000);
}
// Change to a transparent address
{
auto builder = TransactionBuilder(consensusParams, 1, &keystore);
builder.AddTransparentInput(COutPoint(), scriptPubKey, 25000);
ASSERT_TRUE(builder.SendChangeTo(taddr));
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
EXPECT_EQ(tx.vin.size(), 1);
EXPECT_EQ(tx.vout.size(), 1);
EXPECT_EQ(tx.vjoinsplit.size(), 0);
EXPECT_EQ(tx.vShieldedSpend.size(), 0);
EXPECT_EQ(tx.vShieldedOutput.size(), 0);
EXPECT_EQ(tx.valueBalance, 0);
EXPECT_EQ(tx.vout[0].nValue, 15000);
}
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}
TEST(TransactionBuilder, SetFee)
{
SelectParams(CBaseChainParams::REGTEST);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
auto consensusParams = Params().GetConsensus();
// Generate dummy Sapling address
auto sk = libzcash::SaplingSpendingKey::random();
auto expsk = sk.expanded_spending_key();
auto fvk = sk.full_viewing_key();
auto pk = sk.default_address();
// Generate dummy Sapling note
libzcash::SaplingNote note(pk, 50000);
auto cm = note.cm().value();
SaplingMerkleTree tree;
tree.append(cm);
auto anchor = tree.root();
auto witness = tree.witness();
// Default fee
{
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 25000, {});
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
EXPECT_EQ(tx.vin.size(), 0);
EXPECT_EQ(tx.vout.size(), 0);
EXPECT_EQ(tx.vjoinsplit.size(), 0);
EXPECT_EQ(tx.vShieldedSpend.size(), 1);
EXPECT_EQ(tx.vShieldedOutput.size(), 2);
EXPECT_EQ(tx.valueBalance, 10000);
}
// Configured fee
{
auto builder = TransactionBuilder(consensusParams, 1);
ASSERT_TRUE(builder.AddSaplingSpend(expsk, note, anchor, witness));
builder.AddSaplingOutput(fvk, pk, 25000, {});
builder.SetFee(20000);
auto maybe_tx = builder.Build();
ASSERT_EQ(static_cast<bool>(maybe_tx), true);
auto tx = maybe_tx.get();
EXPECT_EQ(tx.vin.size(), 0);
EXPECT_EQ(tx.vout.size(), 0);
EXPECT_EQ(tx.vjoinsplit.size(), 0);
EXPECT_EQ(tx.vShieldedSpend.size(), 1);
EXPECT_EQ(tx.vShieldedOutput.size(), 2);
EXPECT_EQ(tx.valueBalance, 20000);
}
// Revert to default
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
}

View File

@@ -21,11 +21,15 @@ class FakeCoinsViewDB : public CCoinsView {
public:
FakeCoinsViewDB() {}
bool GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
bool GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const {
return false;
}
bool GetNullifier(const uint256 &nf) const {
bool GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const {
return false;
}
bool GetNullifier(const uint256 &nf, ShieldedType type) const {
return false;
}
@@ -42,16 +46,19 @@ public:
return a;
}
uint256 GetBestAnchor() const {
uint256 GetBestAnchor(ShieldedType type) const {
uint256 a;
return a;
}
bool BatchWrite(CCoinsMap &mapCoins,
const uint256 &hashBlock,
const uint256 &hashAnchor,
CAnchorsMap &mapAnchors,
CNullifiersMap &mapNullifiers) {
const uint256 &hashSproutAnchor,
const uint256 &hashSaplingAnchor,
CAnchorsSproutMap &mapSproutAnchors,
CAnchorsSaplingMap &mapSaplingAnchors,
CNullifiersMap &mapSproutNullifiers,
CNullifiersMap saplingNullifiersMap) {
return false;
}
@@ -71,14 +78,16 @@ TEST(Validation, ContextualCheckInputsPassesWithCoinbase) {
FakeCoinsViewDB fakeDB;
CCoinsViewCache view(&fakeDB);
auto consensusBranchId = SPROUT_BRANCH_ID;
CValidationState state;
PrecomputedTransactionData txdata(tx);
EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, txdata, Params(CBaseChainParams::MAIN).GetConsensus(), consensusBranchId));
for (int idx = Consensus::BASE_SPROUT; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) {
auto consensusBranchId = NetworkUpgradeInfo[idx].nBranchId;
CValidationState state;
PrecomputedTransactionData txdata(tx);
EXPECT_TRUE(ContextualCheckInputs(tx, state, view, false, 0, false, txdata, Params(CBaseChainParams::MAIN).GetConsensus(), consensusBranchId));
}
}
TEST(Validation, ReceivedBlockTransactions) {
auto sk = libzcash::SpendingKey::random();
auto sk = libzcash::SproutSpendingKey::random();
// Create a fake genesis block
CBlock block1;

134
src/gtest/test_zip32.cpp Normal file
View File

@@ -0,0 +1,134 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <zcash/zip32.h>
// From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/sapling_zip32.py
// Sapling consistently uses little-endian encoding, but uint256S takes its input in
// big-endian byte order, so the test vectors below are byte-reversed.
TEST(ZIP32, TestVectors) {
std::vector<unsigned char, secure_allocator<unsigned char>> rawSeed {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
HDSeed seed(rawSeed);
auto m = libzcash::SaplingExtendedSpendingKey::Master(seed);
EXPECT_EQ(m.depth, 0);
EXPECT_EQ(m.parentFVKTag, 0);
EXPECT_EQ(m.childIndex, 0);
EXPECT_EQ(
m.chaincode,
uint256S("8e661820750d557e8b34733ebf7ecdfdf31c6d27724fb47aa372bf034b7c94d0"));
EXPECT_EQ(
m.expsk.ask,
uint256S("06257454c907f6510ba1c1830ebf60657760a8869ee968a2b93260d3930cc0b6"));
EXPECT_EQ(
m.expsk.nsk,
uint256S("06ea21888a749fd38eb443d20a030abd2e6e997f5db4f984bd1f2f3be8ed0482"));
EXPECT_EQ(
m.expsk.ovk,
uint256S("21fb4adfa42183848306ffb27719f27d76cf9bb81d023c93d4b9230389845839"));
EXPECT_EQ(
m.dk,
uint256S("72a196f93e8abc0935280ea2a96fa57d6024c9913e0f9fb3af96775bb77cc177"));
EXPECT_THAT(
m.ToXFVK().DefaultAddress().d,
testing::ElementsAreArray({ 0xd8, 0x62, 0x1b, 0x98, 0x1c, 0xf3, 0x00, 0xe9, 0xd4, 0xcc, 0x89 }));
auto m_1 = m.Derive(1);
EXPECT_EQ(m_1.depth, 1);
EXPECT_EQ(m_1.parentFVKTag, 0x3a71c214);
EXPECT_EQ(m_1.childIndex, 1);
EXPECT_EQ(
m_1.chaincode,
uint256S("e6bcda05678a43fad229334ef0b795a590e7c50590baf0d9b9031a690c114701"));
EXPECT_EQ(
m_1.expsk.ask,
uint256S("0c357a2655b4b8d761794095df5cb402d3ba4a428cf6a88e7c2816a597c12b28"));
EXPECT_EQ(
m_1.expsk.nsk,
uint256S("01ba6bff1018fd4eac04da7e3f2c6be9c229e662c5c4d1d6fc1ecafd8829a3e7"));
EXPECT_EQ(
m_1.expsk.ovk,
uint256S("7474a4c518551bd82f14a7f7365a8ffa403c50cfeffedf026ada8688fc81135f"));
EXPECT_EQ(
m_1.dk,
uint256S("dcb4c170d878510e96c4a74192d7eecde9c9912b00b99a12ec91d7a232e84de0"));
EXPECT_THAT(
m_1.ToXFVK().DefaultAddress().d,
testing::ElementsAreArray({ 0x8b, 0x41, 0x38, 0x32, 0x0d, 0xfa, 0xfd, 0x7b, 0x39, 0x97, 0x81 }));
auto m_1_2h = m_1.Derive(2 | ZIP32_HARDENED_KEY_LIMIT);
EXPECT_EQ(m_1_2h.depth, 2);
EXPECT_EQ(m_1_2h.parentFVKTag, 0x079e99db);
EXPECT_EQ(m_1_2h.childIndex, 2 | ZIP32_HARDENED_KEY_LIMIT);
EXPECT_EQ(
m_1_2h.chaincode,
uint256S("35d4a883737742ca41a4baa92323bdb3c93dcb3b462a26b039971bedf415ce97"));
EXPECT_EQ(
m_1_2h.expsk.ask,
uint256S("0dc6e4fe846bda925c82e632980434e17b51dac81fc4821fa71334ee3c11e88b"));
EXPECT_EQ(
m_1_2h.expsk.nsk,
uint256S("0c99a63a275c1c66734761cfb9c62fe9bd1b953f579123d3d0e769c59d057837"));
EXPECT_EQ(
m_1_2h.expsk.ovk,
uint256S("bc1328fc5eb693e18875c5149d06953b11d39447ebd6e38c023c22962e1881cf"));
EXPECT_EQ(
m_1_2h.dk,
uint256S("377bb062dce7e0dcd8a0054d0ca4b4d1481b3710bfa1df12ca46ff9e9fa1eda3"));
EXPECT_THAT(
m_1_2h.ToXFVK().DefaultAddress().d,
testing::ElementsAreArray({ 0xe8, 0xd0, 0x37, 0x93, 0xcd, 0xd2, 0xba, 0xcc, 0x9c, 0x70, 0x41 }));
auto m_1_2hv = m_1_2h.ToXFVK();
EXPECT_EQ(m_1_2hv.depth, 2);
EXPECT_EQ(m_1_2hv.parentFVKTag, 0x079e99db);
EXPECT_EQ(m_1_2hv.childIndex, 2 | ZIP32_HARDENED_KEY_LIMIT);
EXPECT_EQ(
m_1_2hv.chaincode,
uint256S("35d4a883737742ca41a4baa92323bdb3c93dcb3b462a26b039971bedf415ce97"));
EXPECT_EQ(
m_1_2hv.fvk.ak,
uint256S("4138cffdf7200e52d4e9f4384481b4a4c4d070493a5e401e4ffa850f5a92c5a6"));
EXPECT_EQ(
m_1_2hv.fvk.nk,
uint256S("11eee22577304f660cc036bc84b3fc88d1ec50ae8a4d657beb6b211659304e30"));
EXPECT_EQ(
m_1_2hv.fvk.ovk,
uint256S("bc1328fc5eb693e18875c5149d06953b11d39447ebd6e38c023c22962e1881cf"));
EXPECT_EQ(
m_1_2hv.dk,
uint256S("377bb062dce7e0dcd8a0054d0ca4b4d1481b3710bfa1df12ca46ff9e9fa1eda3"));
EXPECT_EQ(m_1_2hv.DefaultAddress(), m_1_2h.ToXFVK().DefaultAddress());
// Hardened derivation from an xfvk fails
EXPECT_FALSE(m_1_2hv.Derive(3 | ZIP32_HARDENED_KEY_LIMIT));
// Non-hardened derivation succeeds
auto maybe_m_1_2hv_3 = m_1_2hv.Derive(3);
EXPECT_TRUE(maybe_m_1_2hv_3);
auto m_1_2hv_3 = maybe_m_1_2hv_3.get();
EXPECT_EQ(m_1_2hv_3.depth, 3);
EXPECT_EQ(m_1_2hv_3.parentFVKTag, 0x7583c148);
EXPECT_EQ(m_1_2hv_3.childIndex, 3);
EXPECT_EQ(
m_1_2hv_3.chaincode,
uint256S("e8e7d6a74a5a1c05be41baec7998d91f7b3603a4c0af495b0d43ba81cf7b938d"));
EXPECT_EQ(
m_1_2hv_3.fvk.ak,
uint256S("a3a697bdda9d648d32a97553de4754b2fac866d726d3f2c436259c507bc585b1"));
EXPECT_EQ(
m_1_2hv_3.fvk.nk,
uint256S("4f66c0814b769963f3bf1bc001270b50edabb27de042fc8a5607d2029e0488db"));
EXPECT_EQ(
m_1_2hv_3.fvk.ovk,
uint256S("f61a699934dc78441324ef628b4b4721611571e8ee3bd591eb3d4b1cfae0b969"));
EXPECT_EQ(
m_1_2hv_3.dk,
uint256S("6ee53b1261f2c9c0f7359ab236f87b52a0f1b0ce43305cdad92ebb63c350cbbe"));
EXPECT_THAT(
m_1_2hv_3.DefaultAddress().d,
testing::ElementsAreArray({ 0x03, 0x0f, 0xfb, 0x26, 0x3a, 0x93, 0x9e, 0x23, 0x0e, 0x96, 0xdd }));
}