2016-04-13 15:52:24 -07:00
|
|
|
#include <gtest/gtest.h>
|
2016-06-15 16:01:55 -07:00
|
|
|
#include <gmock/gmock.h>
|
2016-06-23 15:35:31 -07:00
|
|
|
#include <sodium.h>
|
2016-04-13 15:52:24 -07:00
|
|
|
|
|
|
|
#include "main.h"
|
|
|
|
#include "primitives/transaction.h"
|
|
|
|
#include "consensus/validation.h"
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, check_vpub_not_both_nonzero) {
|
|
|
|
CMutableTransaction tx;
|
|
|
|
tx.nVersion = 2;
|
|
|
|
|
|
|
|
{
|
2016-07-14 15:51:36 -07:00
|
|
|
// Ensure that values within the joinsplit are well-formed.
|
2016-04-13 15:52:24 -07:00
|
|
|
CMutableTransaction newTx(tx);
|
|
|
|
CValidationState state;
|
|
|
|
|
2016-07-10 22:08:20 -07:00
|
|
|
newTx.vjoinsplit.push_back(JSDescription());
|
2016-04-13 15:52:24 -07:00
|
|
|
|
2016-07-14 15:51:36 -07:00
|
|
|
JSDescription *jsdesc = &newTx.vjoinsplit[0];
|
|
|
|
jsdesc->vpub_old = 1;
|
|
|
|
jsdesc->vpub_new = 1;
|
2016-04-13 15:52:24 -07:00
|
|
|
|
2016-06-23 15:35:31 -07:00
|
|
|
EXPECT_FALSE(CheckTransactionWithoutProofVerification(newTx, state));
|
2016-04-13 15:52:24 -07:00
|
|
|
EXPECT_EQ(state.GetRejectReason(), "bad-txns-vpubs-both-nonzero");
|
|
|
|
}
|
|
|
|
}
|
2016-06-15 16:01:55 -07:00
|
|
|
|
|
|
|
class MockCValidationState : public CValidationState {
|
|
|
|
public:
|
|
|
|
MOCK_METHOD5(DoS, bool(int level, bool ret,
|
|
|
|
unsigned char chRejectCodeIn, std::string strRejectReasonIn,
|
|
|
|
bool corruptionIn));
|
|
|
|
MOCK_METHOD3(Invalid, bool(bool ret,
|
|
|
|
unsigned char _chRejectCode, std::string _strRejectReason));
|
|
|
|
MOCK_METHOD1(Error, bool(std::string strRejectReasonIn));
|
|
|
|
MOCK_CONST_METHOD0(IsValid, bool());
|
|
|
|
MOCK_CONST_METHOD0(IsInvalid, bool());
|
|
|
|
MOCK_CONST_METHOD0(IsError, bool());
|
|
|
|
MOCK_CONST_METHOD1(IsInvalid, bool(int &nDoSOut));
|
|
|
|
MOCK_CONST_METHOD0(CorruptionPossible, bool());
|
|
|
|
MOCK_CONST_METHOD0(GetRejectCode, unsigned char());
|
|
|
|
MOCK_CONST_METHOD0(GetRejectReason, std::string());
|
|
|
|
};
|
|
|
|
|
2018-04-26 06:18:36 -07:00
|
|
|
void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranchId);
|
2016-06-22 16:25:35 -07:00
|
|
|
|
|
|
|
CMutableTransaction GetValidTransaction() {
|
2018-02-01 17:49:42 -08:00
|
|
|
uint32_t consensusBranchId = SPROUT_BRANCH_ID;
|
|
|
|
|
2016-06-22 16:25:35 -07:00
|
|
|
CMutableTransaction mtx;
|
|
|
|
mtx.vin.resize(2);
|
2016-06-23 15:59:00 -07:00
|
|
|
mtx.vin[0].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
|
2016-06-22 16:25:35 -07:00
|
|
|
mtx.vin[0].prevout.n = 0;
|
2016-06-23 15:59:00 -07:00
|
|
|
mtx.vin[1].prevout.hash = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
|
2016-06-22 16:25:35 -07:00
|
|
|
mtx.vin[1].prevout.n = 0;
|
|
|
|
mtx.vout.resize(2);
|
|
|
|
// mtx.vout[0].scriptPubKey =
|
|
|
|
mtx.vout[0].nValue = 0;
|
|
|
|
mtx.vout[1].nValue = 0;
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit.resize(2);
|
|
|
|
mtx.vjoinsplit[0].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
|
|
|
mtx.vjoinsplit[0].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000001");
|
|
|
|
mtx.vjoinsplit[1].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000002");
|
|
|
|
mtx.vjoinsplit[1].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000003");
|
2016-06-23 15:35:31 -07:00
|
|
|
|
2018-04-26 06:18:36 -07:00
|
|
|
CreateJoinSplitSignature(mtx, consensusBranchId);
|
|
|
|
return mtx;
|
|
|
|
}
|
2016-06-23 15:35:31 -07:00
|
|
|
|
2018-04-26 06:18:36 -07:00
|
|
|
void CreateJoinSplitSignature(CMutableTransaction& mtx, uint32_t consensusBranchId) {
|
2016-06-23 15:35:31 -07:00
|
|
|
// Generate an ephemeral keypair.
|
|
|
|
uint256 joinSplitPubKey;
|
|
|
|
unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES];
|
|
|
|
crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey);
|
|
|
|
mtx.joinSplitPubKey = joinSplitPubKey;
|
|
|
|
|
|
|
|
// Compute the correct hSig.
|
|
|
|
// TODO: #966.
|
|
|
|
static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
|
|
|
// Empty output script.
|
|
|
|
CScript scriptCode;
|
|
|
|
CTransaction signTx(mtx);
|
2018-02-01 17:49:42 -08:00
|
|
|
uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId);
|
2016-06-23 15:35:31 -07:00
|
|
|
if (dataToBeSigned == one) {
|
|
|
|
throw std::runtime_error("SignatureHash failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the signature
|
|
|
|
assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL,
|
|
|
|
dataToBeSigned.begin(), 32,
|
|
|
|
joinSplitPrivKey
|
|
|
|
) == 0);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, valid_transaction) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
2016-06-23 15:35:31 -07:00
|
|
|
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
2016-10-21 21:07:50 -07:00
|
|
|
TEST(checktransaction_tests, BadVersionTooLow) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.nVersion = 0;
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
2016-10-22 05:26:44 -07:00
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1);
|
2016-10-21 21:07:50 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
2016-06-22 16:25:35 -07:00
|
|
|
TEST(checktransaction_tests, bad_txns_vin_empty) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit.resize(0);
|
2016-06-22 16:25:35 -07:00
|
|
|
mtx.vin.resize(0);
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
2016-06-15 16:01:55 -07:00
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-vin-empty", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-15 16:01:55 -07:00
|
|
|
}
|
2016-06-22 16:25:35 -07:00
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_vout_empty) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit.resize(0);
|
2016-06-22 16:25:35 -07:00
|
|
|
mtx.vout.resize(0);
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
2018-04-26 06:18:36 -07:00
|
|
|
TEST(checktransaction_tests, BadTxnsOversize) {
|
|
|
|
SelectParams(CBaseChainParams::REGTEST);
|
2016-06-22 16:25:35 -07:00
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
|
|
|
|
mtx.vin[0].scriptSig = CScript();
|
|
|
|
std::vector<unsigned char> vchData(520);
|
2016-10-07 23:00:23 -07:00
|
|
|
for (unsigned int i = 0; i < 190; ++i)
|
2016-06-22 16:25:35 -07:00
|
|
|
mtx.vin[0].scriptSig << vchData << OP_DROP;
|
|
|
|
mtx.vin[0].scriptSig << OP_1;
|
|
|
|
|
2016-10-07 23:00:23 -07:00
|
|
|
{
|
|
|
|
// Transaction is just under the limit...
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
CValidationState state;
|
|
|
|
ASSERT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
|
|
|
}
|
2016-06-22 16:25:35 -07:00
|
|
|
|
2016-10-07 23:00:23 -07:00
|
|
|
// Not anymore!
|
|
|
|
mtx.vin[1].scriptSig << vchData << OP_DROP;
|
|
|
|
mtx.vin[1].scriptSig << OP_1;
|
|
|
|
|
|
|
|
{
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
ASSERT_EQ(::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION), 100202);
|
|
|
|
|
2018-04-26 06:18:36 -07:00
|
|
|
// Passes non-contextual checks...
|
2016-10-07 23:00:23 -07:00
|
|
|
MockCValidationState state;
|
2018-04-26 06:18:36 -07:00
|
|
|
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
|
|
|
|
|
|
|
// ... but fails contextual ones!
|
2016-10-07 23:00:23 -07:00
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-oversize", false)).Times(1);
|
2018-04-26 06:18:36 -07:00
|
|
|
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);
|
2016-10-07 23:00:23 -07:00
|
|
|
}
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
2018-05-01 12:15:04 -07:00
|
|
|
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);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-06-22 16:25:35 -07:00
|
|
|
TEST(checktransaction_tests, bad_txns_vout_negative) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vout[0].nValue = -1;
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vout-negative", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_vout_toolarge) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vout[0].nValue = MAX_MONEY + 1;
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vout-toolarge", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_outputs) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vout[0].nValue = MAX_MONEY;
|
|
|
|
mtx.vout[1].nValue = 1;
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_txouttotal_toolarge_joinsplit) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vout[0].nValue = 1;
|
2016-09-05 11:18:43 -07:00
|
|
|
mtx.vjoinsplit[0].vpub_old = MAX_MONEY;
|
2016-06-22 16:25:35 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txouttotal-toolarge", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
2016-09-05 11:18:43 -07:00
|
|
|
TEST(checktransaction_tests, bad_txns_txintotal_toolarge_joinsplit) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vjoinsplit[0].vpub_new = MAX_MONEY - 1;
|
|
|
|
mtx.vjoinsplit[1].vpub_new = MAX_MONEY - 1;
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-txintotal-toolarge", false)).Times(1);
|
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
2016-06-22 16:25:35 -07:00
|
|
|
TEST(checktransaction_tests, bad_txns_vpub_old_negative) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit[0].vpub_old = -1;
|
2016-06-22 16:25:35 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_old-negative", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_vpub_new_negative) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit[0].vpub_new = -1;
|
2016-06-22 16:25:35 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_new-negative", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_vpub_old_toolarge) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit[0].vpub_old = MAX_MONEY + 1;
|
2016-06-22 16:25:35 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_old-toolarge", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_vpub_new_toolarge) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit[0].vpub_new = MAX_MONEY + 1;
|
2016-06-22 16:25:35 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpub_new-toolarge", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_vpubs_both_nonzero) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit[0].vpub_old = 1;
|
|
|
|
mtx.vjoinsplit[0].vpub_new = 1;
|
2016-06-22 16:25:35 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-vpubs-both-nonzero", false)).Times(1);
|
2016-06-23 15:35:31 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
2016-06-22 16:25:35 -07:00
|
|
|
}
|
2016-06-23 15:59:00 -07:00
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_inputs_duplicate) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vin[1].prevout.hash = mtx.vin[0].prevout.hash;
|
|
|
|
mtx.vin[1].prevout.n = mtx.vin[0].prevout.n;
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate", false)).Times(1);
|
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
2016-07-14 15:51:36 -07:00
|
|
|
TEST(checktransaction_tests, bad_joinsplits_nullifiers_duplicate_same_joinsplit) {
|
2016-06-23 15:59:00 -07:00
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit[0].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
|
|
|
mtx.vjoinsplit[0].nullifiers.at(1) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
2016-06-23 15:59:00 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
2016-07-14 15:51:36 -07:00
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false)).Times(1);
|
2016-06-23 15:59:00 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
2016-07-14 15:51:36 -07:00
|
|
|
TEST(checktransaction_tests, bad_joinsplits_nullifiers_duplicate_different_joinsplit) {
|
2016-06-23 15:59:00 -07:00
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit[0].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
|
|
|
mtx.vjoinsplit[1].nullifiers.at(0) = uint256S("0000000000000000000000000000000000000000000000000000000000000000");
|
2016-06-23 15:59:00 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
2016-07-14 15:51:36 -07:00
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-joinsplits-nullifiers-duplicate", false)).Times(1);
|
2016-06-23 15:59:00 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
2016-07-14 15:51:36 -07:00
|
|
|
TEST(checktransaction_tests, bad_cb_has_joinsplits) {
|
2016-06-23 15:59:00 -07:00
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
// Make it a coinbase.
|
|
|
|
mtx.vin.resize(1);
|
|
|
|
mtx.vin[0].prevout.SetNull();
|
|
|
|
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit.resize(1);
|
2016-06-23 15:59:00 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
EXPECT_TRUE(tx.IsCoinBase());
|
|
|
|
|
|
|
|
MockCValidationState state;
|
2016-07-14 15:51:36 -07:00
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-has-joinsplits", false)).Times(1);
|
2016-06-23 15:59:00 -07:00
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_cb_empty_scriptsig) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
// Make it a coinbase.
|
|
|
|
mtx.vin.resize(1);
|
|
|
|
mtx.vin[0].prevout.SetNull();
|
|
|
|
|
2016-07-14 13:16:59 -07:00
|
|
|
mtx.vjoinsplit.resize(0);
|
2016-06-23 15:59:00 -07:00
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
EXPECT_TRUE(tx.IsCoinBase());
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-cb-length", false)).Times(1);
|
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_prevout_null) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vin[1].prevout.SetNull();
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
EXPECT_FALSE(tx.IsCoinBase());
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null", false)).Times(1);
|
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, bad_txns_invalid_joinsplit_signature) {
|
2018-02-01 17:49:42 -08:00
|
|
|
SelectParams(CBaseChainParams::REGTEST);
|
|
|
|
|
2016-06-23 15:59:00 -07:00
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.joinSplitSig[0] += 1;
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
|
2018-02-01 17:49:42 -08:00
|
|
|
ContextualCheckTransaction(tx, state, 0, 100);
|
2016-06-23 15:59:00 -07:00
|
|
|
}
|
2016-06-23 17:22:20 -07:00
|
|
|
|
|
|
|
TEST(checktransaction_tests, non_canonical_ed25519_signature) {
|
2018-02-01 17:49:42 -08:00
|
|
|
SelectParams(CBaseChainParams::REGTEST);
|
|
|
|
|
2016-06-23 17:22:20 -07:00
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
|
2016-08-18 15:38:20 -07:00
|
|
|
// Check that the signature is valid before we add L
|
|
|
|
{
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
2018-02-01 17:49:42 -08:00
|
|
|
EXPECT_TRUE(ContextualCheckTransaction(tx, state, 0, 100));
|
2016-08-18 15:38:20 -07:00
|
|
|
}
|
|
|
|
|
2016-06-23 17:22:20 -07:00
|
|
|
// Copied from libsodium/crypto_sign/ed25519/ref10/open.c
|
|
|
|
static const unsigned char L[32] =
|
|
|
|
{ 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58,
|
|
|
|
0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 };
|
|
|
|
|
|
|
|
// Add L to S, which starts at mtx.joinSplitSig[32].
|
|
|
|
unsigned int s = 0;
|
|
|
|
for (size_t i = 0; i < 32; i++) {
|
|
|
|
s = mtx.joinSplitSig[32 + i] + L[i] + (s >> 8);
|
|
|
|
mtx.joinSplitSig[32 + i] = s & 0xff;
|
|
|
|
}
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
|
|
|
|
MockCValidationState state;
|
2016-08-18 15:38:20 -07:00
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-invalid-joinsplit-signature", false)).Times(1);
|
2018-02-01 17:49:42 -08:00
|
|
|
ContextualCheckTransaction(tx, state, 0, 100);
|
2016-06-23 17:22:20 -07:00
|
|
|
}
|
2018-02-15 22:19:36 -08:00
|
|
|
|
|
|
|
TEST(checktransaction_tests, OverwinterConstructors) {
|
|
|
|
CMutableTransaction mtx;
|
|
|
|
mtx.fOverwintered = true;
|
2018-04-23 08:54:18 -07:00
|
|
|
mtx.nVersion = OVERWINTER_TX_VERSION;
|
2018-02-15 22:19:36 -08:00
|
|
|
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
|
|
|
mtx.nExpiryHeight = 20;
|
|
|
|
|
|
|
|
// Check constructor with overwinter fields
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
EXPECT_EQ(tx.nVersion, mtx.nVersion);
|
|
|
|
EXPECT_EQ(tx.fOverwintered, mtx.fOverwintered);
|
|
|
|
EXPECT_EQ(tx.nVersionGroupId, mtx.nVersionGroupId);
|
|
|
|
EXPECT_EQ(tx.nExpiryHeight, mtx.nExpiryHeight);
|
|
|
|
|
|
|
|
// Check constructor of mutable transaction struct
|
|
|
|
CMutableTransaction mtx2(tx);
|
|
|
|
EXPECT_EQ(mtx2.nVersion, mtx.nVersion);
|
|
|
|
EXPECT_EQ(mtx2.fOverwintered, mtx.fOverwintered);
|
|
|
|
EXPECT_EQ(mtx2.nVersionGroupId, mtx.nVersionGroupId);
|
|
|
|
EXPECT_EQ(mtx2.nExpiryHeight, mtx.nExpiryHeight);
|
|
|
|
EXPECT_TRUE(mtx2.GetHash() == mtx.GetHash());
|
|
|
|
|
|
|
|
// Check assignment of overwinter fields
|
|
|
|
CTransaction tx2 = tx;
|
|
|
|
EXPECT_EQ(tx2.nVersion, mtx.nVersion);
|
|
|
|
EXPECT_EQ(tx2.fOverwintered, mtx.fOverwintered);
|
|
|
|
EXPECT_EQ(tx2.nVersionGroupId, mtx.nVersionGroupId);
|
|
|
|
EXPECT_EQ(tx2.nExpiryHeight, mtx.nExpiryHeight);
|
|
|
|
EXPECT_TRUE(tx2 == tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, OverwinterSerialization) {
|
|
|
|
CMutableTransaction mtx;
|
|
|
|
mtx.fOverwintered = true;
|
2018-04-23 08:54:18 -07:00
|
|
|
mtx.nVersion = OVERWINTER_TX_VERSION;
|
2018-02-15 22:19:36 -08:00
|
|
|
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
|
|
|
mtx.nExpiryHeight = 99;
|
|
|
|
|
|
|
|
// Check round-trip serialization and deserialization from mtx to tx.
|
|
|
|
{
|
|
|
|
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
|
|
|
|
ss << mtx;
|
|
|
|
CTransaction tx;
|
|
|
|
ss >> tx;
|
|
|
|
EXPECT_EQ(mtx.nVersion, tx.nVersion);
|
|
|
|
EXPECT_EQ(mtx.fOverwintered, tx.fOverwintered);
|
|
|
|
EXPECT_EQ(mtx.nVersionGroupId, tx.nVersionGroupId);
|
|
|
|
EXPECT_EQ(mtx.nExpiryHeight, tx.nExpiryHeight);
|
|
|
|
|
|
|
|
EXPECT_EQ(mtx.GetHash(), CMutableTransaction(tx).GetHash());
|
|
|
|
EXPECT_EQ(tx.GetHash(), CTransaction(mtx).GetHash());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Also check mtx to mtx
|
|
|
|
{
|
|
|
|
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
|
|
|
|
ss << mtx;
|
|
|
|
CMutableTransaction mtx2;
|
|
|
|
ss >> mtx2;
|
|
|
|
EXPECT_EQ(mtx.nVersion, mtx2.nVersion);
|
|
|
|
EXPECT_EQ(mtx.fOverwintered, mtx2.fOverwintered);
|
|
|
|
EXPECT_EQ(mtx.nVersionGroupId, mtx2.nVersionGroupId);
|
|
|
|
EXPECT_EQ(mtx.nExpiryHeight, mtx2.nExpiryHeight);
|
|
|
|
|
|
|
|
EXPECT_EQ(mtx.GetHash(), mtx2.GetHash());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Also check tx to tx
|
|
|
|
{
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
|
|
|
|
ss << tx;
|
|
|
|
CTransaction tx2;
|
|
|
|
ss >> tx2;
|
|
|
|
EXPECT_EQ(tx.nVersion, tx2.nVersion);
|
|
|
|
EXPECT_EQ(tx.fOverwintered, tx2.fOverwintered);
|
|
|
|
EXPECT_EQ(tx.nVersionGroupId, tx2.nVersionGroupId);
|
|
|
|
EXPECT_EQ(tx.nExpiryHeight, tx2.nExpiryHeight);
|
|
|
|
|
|
|
|
EXPECT_EQ(mtx.GetHash(), CMutableTransaction(tx).GetHash());
|
|
|
|
EXPECT_EQ(tx.GetHash(), tx2.GetHash());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, OverwinterDefaultValues) {
|
|
|
|
// Check default values (this will fail when defaults change; test should then be updated)
|
|
|
|
CTransaction tx;
|
|
|
|
EXPECT_EQ(tx.nVersion, 1);
|
|
|
|
EXPECT_EQ(tx.fOverwintered, false);
|
|
|
|
EXPECT_EQ(tx.nVersionGroupId, 0);
|
|
|
|
EXPECT_EQ(tx.nExpiryHeight, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// A valid v3 transaction with no joinsplits
|
|
|
|
TEST(checktransaction_tests, OverwinterValidTx) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vjoinsplit.resize(0);
|
|
|
|
mtx.fOverwintered = true;
|
2018-04-23 08:54:18 -07:00
|
|
|
mtx.nVersion = OVERWINTER_TX_VERSION;
|
2018-02-15 22:19:36 -08:00
|
|
|
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
|
|
|
mtx.nExpiryHeight = 0;
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(checktransaction_tests, OverwinterExpiryHeight) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vjoinsplit.resize(0);
|
|
|
|
mtx.fOverwintered = true;
|
2018-04-23 08:54:18 -07:00
|
|
|
mtx.nVersion = OVERWINTER_TX_VERSION;
|
2018-02-15 22:19:36 -08:00
|
|
|
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
|
|
|
mtx.nExpiryHeight = 0;
|
|
|
|
|
|
|
|
{
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
mtx.nExpiryHeight = TX_EXPIRY_HEIGHT_THRESHOLD - 1;
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_TRUE(CheckTransactionWithoutProofVerification(tx, state));
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
mtx.nExpiryHeight = TX_EXPIRY_HEIGHT_THRESHOLD;
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-expiry-height-too-high", false)).Times(1);
|
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
mtx.nExpiryHeight = std::numeric_limits<uint32_t>::max();
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-expiry-height-too-high", false)).Times(1);
|
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Test that a Sprout tx with a negative version number is detected
|
|
|
|
// given the new Overwinter logic
|
|
|
|
TEST(checktransaction_tests, SproutTxVersionTooLow) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vjoinsplit.resize(0);
|
|
|
|
mtx.fOverwintered = false;
|
|
|
|
mtx.nVersion = -1;
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-txns-version-too-low", false)).Times(1);
|
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Subclass of CTransaction which doesn't call UpdateHash when constructing
|
|
|
|
// from a CMutableTransaction. This enables us to create a CTransaction
|
|
|
|
// with bad values which normally trigger an exception during construction.
|
|
|
|
class UNSAFE_CTransaction : public CTransaction {
|
|
|
|
public:
|
|
|
|
UNSAFE_CTransaction(const CMutableTransaction &tx) : CTransaction(tx, true) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Test bad Overwinter version number in CheckTransactionWithoutProofVerification
|
|
|
|
TEST(checktransaction_tests, OverwinterVersionNumberLow) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vjoinsplit.resize(0);
|
|
|
|
mtx.fOverwintered = true;
|
|
|
|
mtx.nVersion = OVERWINTER_MIN_TX_VERSION - 1;
|
|
|
|
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
|
|
|
mtx.nExpiryHeight = 0;
|
|
|
|
|
|
|
|
UNSAFE_CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-low", false)).Times(1);
|
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test bad Overwinter version number in ContextualCheckTransaction
|
|
|
|
TEST(checktransaction_tests, OverwinterVersionNumberHigh) {
|
|
|
|
SelectParams(CBaseChainParams::REGTEST);
|
|
|
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
|
|
|
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vjoinsplit.resize(0);
|
|
|
|
mtx.fOverwintered = true;
|
|
|
|
mtx.nVersion = OVERWINTER_MAX_TX_VERSION + 1;
|
|
|
|
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
|
|
|
mtx.nExpiryHeight = 0;
|
|
|
|
|
|
|
|
UNSAFE_CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-overwinter-version-too-high", false)).Times(1);
|
|
|
|
ContextualCheckTransaction(tx, state, 1, 100);
|
|
|
|
|
|
|
|
// Revert to default
|
|
|
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Test bad Overwinter version group id
|
|
|
|
TEST(checktransaction_tests, OverwinterBadVersionGroupId) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.vjoinsplit.resize(0);
|
|
|
|
mtx.fOverwintered = true;
|
2018-04-23 08:54:18 -07:00
|
|
|
mtx.nVersion = OVERWINTER_TX_VERSION;
|
2018-02-15 22:19:36 -08:00
|
|
|
mtx.nExpiryHeight = 0;
|
|
|
|
mtx.nVersionGroupId = 0x12345678;
|
|
|
|
|
|
|
|
UNSAFE_CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "bad-tx-version-group-id", false)).Times(1);
|
|
|
|
CheckTransactionWithoutProofVerification(tx, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This tests an Overwinter transaction checked against Sprout
|
|
|
|
TEST(checktransaction_tests, OverwinterNotActive) {
|
|
|
|
SelectParams(CBaseChainParams::TESTNET);
|
|
|
|
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.fOverwintered = true;
|
2018-04-23 08:54:18 -07:00
|
|
|
mtx.nVersion = OVERWINTER_TX_VERSION;
|
2018-02-15 22:19:36 -08:00
|
|
|
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);
|
|
|
|
ContextualCheckTransaction(tx, state, 1, 100);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This tests a transaction without the fOverwintered flag set, against the Overwinter consensus rule set.
|
|
|
|
TEST(checktransaction_tests, OverwinterFlagNotSet) {
|
|
|
|
SelectParams(CBaseChainParams::REGTEST);
|
|
|
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::ALWAYS_ACTIVE);
|
|
|
|
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.fOverwintered = false;
|
2018-04-23 08:54:18 -07:00
|
|
|
mtx.nVersion = OVERWINTER_TX_VERSION;
|
2018-02-15 22:19:36 -08:00
|
|
|
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
|
|
|
mtx.nExpiryHeight = 0;
|
|
|
|
|
|
|
|
CTransaction tx(mtx);
|
|
|
|
MockCValidationState state;
|
|
|
|
EXPECT_CALL(state, DoS(100, false, REJECT_INVALID, "tx-overwinter-flag-not-set", false)).Times(1);
|
|
|
|
ContextualCheckTransaction(tx, state, 1, 100);
|
|
|
|
|
|
|
|
// Revert to default
|
|
|
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Overwinter (NU0) does not allow soft fork to version 4 Overwintered tx.
|
|
|
|
TEST(checktransaction_tests, OverwinterInvalidSoftForkVersion) {
|
|
|
|
CMutableTransaction mtx = GetValidTransaction();
|
|
|
|
mtx.fOverwintered = true;
|
|
|
|
mtx.nVersion = 4; // This is not allowed
|
|
|
|
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
|
|
|
|
mtx.nExpiryHeight = 0;
|
|
|
|
|
|
|
|
CDataStream ss(SER_DISK, PROTOCOL_VERSION);
|
|
|
|
try {
|
|
|
|
ss << mtx;
|
|
|
|
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format'";
|
|
|
|
}
|
|
|
|
catch(std::ios_base::failure & err) {
|
|
|
|
EXPECT_THAT(err.what(), testing::HasSubstr(std::string("Unknown transaction format")));
|
|
|
|
}
|
|
|
|
catch(...) {
|
|
|
|
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Test CreateNewContextualCMutableTransaction sets default values based on height
|
|
|
|
TEST(checktransaction_tests, OverwinteredContextualCreateTx) {
|
|
|
|
SelectParams(CBaseChainParams::REGTEST);
|
|
|
|
const Consensus::Params& consensusParams = Params().GetConsensus();
|
|
|
|
int activationHeight = 5;
|
2018-05-03 05:48:20 -07:00
|
|
|
int saplingActivationHeight = 30;
|
2018-02-15 22:19:36 -08:00
|
|
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, activationHeight);
|
2018-05-03 05:48:20 -07:00
|
|
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, saplingActivationHeight);
|
2018-02-15 22:19:36 -08:00
|
|
|
|
|
|
|
{
|
|
|
|
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
|
|
|
|
consensusParams, activationHeight - 1);
|
|
|
|
|
|
|
|
EXPECT_EQ(mtx.nVersion, 1);
|
|
|
|
EXPECT_EQ(mtx.fOverwintered, false);
|
|
|
|
EXPECT_EQ(mtx.nVersionGroupId, 0);
|
|
|
|
EXPECT_EQ(mtx.nExpiryHeight, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overwinter activates
|
|
|
|
{
|
|
|
|
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(
|
|
|
|
consensusParams, activationHeight );
|
|
|
|
|
|
|
|
EXPECT_EQ(mtx.nVersion, 3);
|
|
|
|
EXPECT_EQ(mtx.fOverwintered, true);
|
|
|
|
EXPECT_EQ(mtx.nVersionGroupId, OVERWINTER_VERSION_GROUP_ID);
|
2018-05-03 05:48:20 -07:00
|
|
|
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);
|
2018-02-15 22:19:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Revert to default
|
2018-05-03 05:48:20 -07:00
|
|
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_SAPLING, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
2018-02-15 22:19:36 -08:00
|
|
|
UpdateNetworkUpgradeParameters(Consensus::UPGRADE_OVERWINTER, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT);
|
|
|
|
}
|
2018-02-16 15:42:04 -08:00
|
|
|
|
|
|
|
// Test a v1 transaction which has a malformed header, perhaps modified in-flight
|
|
|
|
TEST(checktransaction_tests, BadTxReceivedOverNetwork)
|
|
|
|
{
|
|
|
|
// First four bytes <01 00 00 00> have been modified to be <FC FF FF FF> (-4 as an int32)
|
|
|
|
std::string goodPrefix = "01000000";
|
|
|
|
std::string badPrefix = "fcffffff";
|
|
|
|
std::string hexTx = "0176c6541939b95f8d8b7779a77a0863b2a0267e281a050148326f0ea07c3608fb000000006a47304402207c68117a6263486281af0cc5d3bee6db565b6dce19ffacc4cb361906eece82f8022007f604382dee2c1fde41c4e6e7c1ae36cfa28b5b27350c4bfaa27f555529eace01210307ff9bef60f2ac4ceb1169a9f7d2c773d6c7f4ab6699e1e5ebc2e0c6d291c733feffffff02c0d45407000000001976a9145eaaf6718517ec8a291c6e64b16183292e7011f788ac5ef44534000000001976a91485e12fb9967c96759eae1c6b1e9c07ce977b638788acbe000000";
|
|
|
|
|
|
|
|
// Good v1 tx
|
|
|
|
{
|
|
|
|
std::vector<unsigned char> txData(ParseHex(goodPrefix + hexTx ));
|
|
|
|
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
CTransaction tx;
|
|
|
|
ssData >> tx;
|
|
|
|
EXPECT_EQ(tx.nVersion, 1);
|
|
|
|
EXPECT_EQ(tx.fOverwintered, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Good v1 mutable tx
|
|
|
|
{
|
|
|
|
std::vector<unsigned char> txData(ParseHex(goodPrefix + hexTx ));
|
|
|
|
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
CMutableTransaction mtx;
|
|
|
|
ssData >> mtx;
|
|
|
|
EXPECT_EQ(mtx.nVersion, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bad tx
|
|
|
|
{
|
|
|
|
std::vector<unsigned char> txData(ParseHex(badPrefix + hexTx ));
|
|
|
|
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
try {
|
|
|
|
CTransaction tx;
|
|
|
|
ssData >> tx;
|
|
|
|
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format'";
|
|
|
|
}
|
|
|
|
catch(std::ios_base::failure & err) {
|
|
|
|
EXPECT_THAT(err.what(), testing::HasSubstr(std::string("Unknown transaction format")));
|
|
|
|
}
|
|
|
|
catch(...) {
|
|
|
|
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bad mutable tx
|
|
|
|
{
|
|
|
|
std::vector<unsigned char> txData(ParseHex(badPrefix + hexTx ));
|
|
|
|
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
try {
|
|
|
|
CMutableTransaction mtx;
|
|
|
|
ssData >> mtx;
|
|
|
|
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format'";
|
|
|
|
}
|
|
|
|
catch(std::ios_base::failure & err) {
|
|
|
|
EXPECT_THAT(err.what(), testing::HasSubstr(std::string("Unknown transaction format")));
|
|
|
|
}
|
|
|
|
catch(...) {
|
|
|
|
FAIL() << "Expected std::ios_base::failure 'Unknown transaction format', got some other exception";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|