Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6267534
[MOVE] rename files specialtx_validation.* --> specialtx.*
random-zebra Jan 25, 2021
a0a5170
[Refactor] Introduce GetUTXO* functions relying on pcoinsTip
random-zebra Jan 25, 2021
211b36d
[Tests] Fix proper sapling tx version in unit tests
random-zebra Feb 11, 2021
69662f8
[Core] Add GetSerializeSizeNetwork for generic (non-array) Optionals
random-zebra Feb 11, 2021
e288fd6
[RPC] Return full debug message for ATMP failures in sendrawtransaction
random-zebra Feb 11, 2021
286a35e
[Refactoring] Make CWallet::FundTransaction atomic
random-zebra Mar 10, 2021
517fe0c
[Trivial][Cleanup] Remove unused variable in ScanForWalletTransactions
random-zebra Mar 10, 2021
9ef8031
[Wallet] Account for extra payload sizes in Fund/CreateTransaction
random-zebra Feb 11, 2021
e1eb3f5
[Cleanup] Remove fSaplingActive from CheckSpecialTx
random-zebra Jan 27, 2021
dab3830
[Tests][Trivial] Remove annoying warning for unintended optimization
random-zebra Feb 13, 2021
11d2025
[Refactoring] move sapling contextual checks out of CheckTransaction
random-zebra Feb 8, 2021
bfd3fe4
[Refactoring] unify contextual checks for txes
random-zebra Feb 8, 2021
63a87f9
[Refactoring] move zerocoin contextual checks out of CheckTransaction
random-zebra Feb 8, 2021
eba2377
[Refactoring] Move CheckSpecialTx out of CheckTransaction
random-zebra Mar 8, 2021
34882e6
[Refactoring][Consensus] Connect SpecialTx processing to validation code
random-zebra Mar 23, 2021
50a55b7
[Cleanup] Remove unused boolean arguments in CheckZerocoinSpend
random-zebra Apr 7, 2021
b408de3
[Refactoring] non-contextual checks for special txes in CheckBlock
random-zebra Apr 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ set(COMMON_SOURCES
./src/coins.cpp
./src/key_io.cpp
./src/compressor.cpp
./src/tiertwo/specialtx_validation.cpp
./src/evo/specialtx.cpp
./src/consensus/merkle.cpp
./src/consensus/zerocoin_verify.cpp
./src/primitives/block.cpp
Expand Down
4 changes: 2 additions & 2 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ BITCOIN_CORE_H = \
cuckoocache.h \
crypter.h \
cyclingvector.h \
evo/specialtx.h \
pairresult.h \
addressbook.h \
wallet/db.h \
Expand Down Expand Up @@ -243,7 +244,6 @@ BITCOIN_CORE_H = \
rpc/protocol.h \
rpc/register.h \
rpc/server.h \
tiertwo/specialtx_validation.h \
scheduler.h \
script/interpreter.h \
script/keyorigin.h \
Expand Down Expand Up @@ -322,6 +322,7 @@ libbitcoin_server_a_SOURCES = \
consensus/params.cpp \
consensus/tx_verify.cpp \
consensus/zerocoin_verify.cpp \
evo/specialtx.cpp \
httprpc.cpp \
httpserver.cpp \
init.cpp \
Expand All @@ -346,7 +347,6 @@ libbitcoin_server_a_SOURCES = \
rpc/net.cpp \
rpc/rawtransaction.cpp \
rpc/server.cpp \
tiertwo/specialtx_validation.cpp \
script/sigcache.cpp \
script/ismine.cpp \
sporkdb.cpp \
Expand Down
3 changes: 3 additions & 0 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ class CMainParams : public CChainParams
consensus.ZC_MinMintFee = 1 * CENT;
consensus.ZC_MinStakeDepth = 200;
consensus.ZC_TimeStart = 1508214600; // October 17, 2017 4:30:00 AM
consensus.ZC_HeightStart = 863735;
Comment thread
furszy marked this conversation as resolved.
Outdated

// Network upgrades
consensus.vUpgrades[Consensus::BASE_NETWORK].nActivationHeight =
Expand Down Expand Up @@ -302,6 +303,7 @@ class CTestNetParams : public CChainParams
consensus.height_last_ZC_AccumCheckpoint = -1;
consensus.height_last_ZC_WrappedSerials = -1;
consensus.height_ZC_RecalcAccumulators = 999999999;
consensus.ZC_HeightStart = 0;

// Zerocoin-related params
consensus.ZC_Modulus = "25195908475657893494027183240048398571429282126204032027777137836043662020707595556264018525880784"
Expand Down Expand Up @@ -441,6 +443,7 @@ class CRegTestParams : public CChainParams
consensus.ZC_MinMintFee = 1 * CENT;
consensus.ZC_MinStakeDepth = 10;
consensus.ZC_TimeStart = 0; // not implemented on regtest
consensus.ZC_HeightStart = 0;

// Network upgrades
consensus.vUpgrades[Consensus::BASE_NETWORK].nActivationHeight =
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ struct Params {
CAmount ZC_MinMintFee;
int ZC_MinStakeDepth;
int ZC_TimeStart;
int ZC_HeightStart;

libzerocoin::ZerocoinParams* Zerocoin_Params(bool useModulusV1) const
{
Expand Down
77 changes: 23 additions & 54 deletions src/consensus/tx_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
#include "tx_verify.h"

#include "consensus/consensus.h"
#include "evo/specialtx.h"
#include "consensus/zerocoin_verify.h"
#include "sapling/sapling_validation.h"
#include "tiertwo/specialtx_validation.h"
#include "../validation.h"

bool IsFinalTx(const CTransactionRef& tx, int nBlockHeight, int64_t nBlockTime)
Expand Down Expand Up @@ -52,7 +52,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in
return nSigOps;
}

bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, CValidationState& state, bool fFakeSerialAttack, bool fColdStakingActive, bool fSaplingActive)
bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fColdStakingActive)
{
// Basic checks that don't depend on any context
// Transactions containing empty `vin` must have non-empty `vShieldedSpend`.
Expand All @@ -63,12 +63,10 @@ bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, CValidationS
return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty");

// Version check
if (fSaplingActive) {
// After sapling activation we require 1 <= tx.nVersion < TxVersion::TOOHIGH
if (tx.nVersion < 1 || tx.nVersion >= CTransaction::TxVersion::TOOHIGH)
return state.DoS(10,
error("%s: Transaction version (%d) too high. Max: %d", __func__, tx.nVersion, int(CTransaction::TxVersion::TOOHIGH) - 1),
REJECT_INVALID, "bad-tx-version-too-high");
if (tx.nVersion < 1 || tx.nVersion >= CTransaction::TxVersion::TOOHIGH) {
return state.DoS(10,
error("%s: Transaction version (%d) too high. Max: %d", __func__, tx.nVersion, int(CTransaction::TxVersion::TOOHIGH) - 1),
REJECT_INVALID, "bad-tx-version-too-high");
}

// Size limits
Expand All @@ -81,12 +79,7 @@ bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, CValidationS

// Dispatch to Sapling validator
CAmount nValueOut = 0;
if (!SaplingValidation::CheckTransaction(tx, state, nValueOut, fSaplingActive)) {
return false;
}

// Dispatch to SpecialTx validator
if (!CheckSpecialTx(tx, state, fSaplingActive)) {
if (!SaplingValidation::CheckTransaction(tx, state, nValueOut)) {
return false;
}

Expand All @@ -112,62 +105,38 @@ bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, CValidationS
}

std::set<COutPoint> vInOutPoints;
std::set<CBigNum> vZerocoinSpendSerials;
int nZCSpendCount = 0;

for (const CTxIn& txin : tx.vin) {
// Check for duplicate inputs
if (vInOutPoints.count(txin.prevout))
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-duplicate");

//duplicate zcspend serials are checked in CheckZerocoinSpend()
if (!txin.IsZerocoinSpend()) {
vInOutPoints.insert(txin.prevout);
} else if (!txin.IsZerocoinPublicSpend()) {
nZCSpendCount++;
}
}

if (fZerocoinActive) {
if (nZCSpendCount > consensus.ZC_MaxSpendsPerTx)
return state.DoS(100, error("CheckTransaction() : there are more zerocoin spends than are allowed in one transaction"));

//require that a zerocoinspend only has inputs that are zerocoins
if (tx.HasZerocoinSpendInputs()) {
for (const CTxIn& in : tx.vin) {
if (!in.IsZerocoinSpend() && !in.IsZerocoinPublicSpend())
return state.DoS(100,
error("CheckTransaction() : zerocoinspend contains inputs that are not zerocoins"));
}

// Do not require signature verification if this is initial sync and a block over 24 hours old
bool fVerifySignature = !IsInitialBlockDownload() && (GetTime() - chainActive.Tip()->GetBlockTime() < (60*60*24));
if (!CheckZerocoinSpend(tx, fVerifySignature, state, fFakeSerialAttack))
return state.DoS(100, error("CheckTransaction() : invalid zerocoin spend"));
Comment thread
furszy marked this conversation as resolved.
Outdated
}
}

if (tx.IsCoinBase()) {
if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 150)
return state.DoS(100, false, REJECT_INVALID, "bad-cb-length");
} else if (fZerocoinActive && tx.HasZerocoinSpendInputs()) {
if (tx.vin.size() < 1)
return state.DoS(10, false, REJECT_INVALID, "bad-zc-spend-min-inputs");
if (tx.HasZerocoinPublicSpendInputs()) {
// tx has public zerocoin spend inputs
if(static_cast<int>(tx.vin.size()) > consensus.ZC_MaxPublicSpendsPerTx)
return state.DoS(10, false, REJECT_INVALID, "bad-zc-spend-max-inputs");
} else {
// tx has regular zerocoin spend inputs
if(static_cast<int>(tx.vin.size()) > consensus.ZC_MaxSpendsPerTx)
return state.DoS(10, false, REJECT_INVALID, "bad-zc-spend-max-inputs");
}

} else {
for (const CTxIn& txin : tx.vin)
if (txin.prevout.IsNull() && (fZerocoinActive && !txin.IsZerocoinSpend()))
if (txin.prevout.IsNull() && !txin.IsZerocoinSpend())
return state.DoS(10, false, REJECT_INVALID, "bad-txns-prevout-null");
}

return true;
}

bool ContextualCheckTransaction(const CTransactionRef& tx, CValidationState& state, const CChainParams& chainparams, int nHeight, bool isMined, bool fIBD)
{
// Dispatch to Sapling validator
if (!SaplingValidation::ContextualCheckTransaction(*tx, state, chainparams, nHeight, isMined, fIBD)) {
return false; // Failure reason has been set in validation state object
}

// Dispatch to ZerocoinTx validator
if (!ContextualCheckZerocoinTx(tx, state, chainparams.GetConsensus(), nHeight)) {
return false; // Failure reason has been set in validation state object
}

return true;
}
5 changes: 4 additions & 1 deletion src/consensus/tx_verify.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
#include <vector>

class CBlockIndex;
class CChainParams;
class CCoinsViewCache;
class CValidationState;

/** Transaction validation functions */

/** Context-independent validity checks */
bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, CValidationState& state, bool fFakeSerialAttack = false, bool fColdStakingActive=false, bool fSaplingActive=false);
bool CheckTransaction(const CTransaction& tx, CValidationState& state, bool fColdStakingActive);
/** Context-dependent validity checks */
bool ContextualCheckTransaction(const CTransactionRef& tx, CValidationState& state, const CChainParams& chainparams, int nHeight, bool isMined, bool fIBD);

/**
* Count ECDSA signature operations the old-fashioned (pre-0.6) way
Expand Down
82 changes: 71 additions & 11 deletions src/consensus/zerocoin_verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
#include "zpiv/zpivmodule.h"


bool CheckZerocoinSpend(const CTransaction& tx, bool fVerifySignature, CValidationState& state, bool fFakeSerialAttack)
static bool CheckZerocoinSpend(const CTransactionRef _tx, CValidationState& state)
{
const CTransaction& tx = *_tx;
//max needed non-mint outputs should be 2 - one for redemption address and a possible 2nd for change
if (tx.vout.size() > 2) {
int outs = 0;
Expand All @@ -29,7 +30,7 @@ bool CheckZerocoinSpend(const CTransaction& tx, bool fVerifySignature, CValidati
outs++;
}
if (outs > 2 && !tx.IsCoinStake())
return state.DoS(100, error("CheckZerocoinSpend(): over two non-mint outputs in a zerocoinspend transaction"));
return state.DoS(100, error("%s: over two non-mint outputs in a zerocoinspend transaction", __func__));
}

//compute the txout hash that is used for the zerocoinspend signatures
Expand All @@ -54,12 +55,12 @@ bool CheckZerocoinSpend(const CTransaction& tx, bool fVerifySignature, CValidati
CTxOut prevOut;
if (isPublicSpend) {
if(!GetOutput(txin.prevout.hash, txin.prevout.n, state, prevOut)){
return state.DoS(100, error("CheckZerocoinSpend(): public zerocoin spend prev output not found, prevTx %s, index %d", txin.prevout.hash.GetHex(), txin.prevout.n));
return state.DoS(100, error("%s: public zerocoin spend prev output not found, prevTx %s, index %d", __func__, txin.prevout.hash.GetHex(), txin.prevout.n));
}
libzerocoin::ZerocoinParams* params = consensus.Zerocoin_Params(false);
PublicCoinSpend publicSpend(params);
if (!ZPIVModule::parseCoinSpend(txin, tx, prevOut, publicSpend)){
return state.DoS(100, error("CheckZerocoinSpend(): public zerocoin spend parse failed"));
return state.DoS(100, error("%s: public zerocoin spend parse failed", __func__));
}
newSpend = publicSpend;
} else {
Expand All @@ -68,26 +69,26 @@ bool CheckZerocoinSpend(const CTransaction& tx, bool fVerifySignature, CValidati

//check that the denomination is valid
if (newSpend.getDenomination() == libzerocoin::ZQ_ERROR)
return state.DoS(100, error("Zerocoinspend does not have the correct denomination"));
return state.DoS(100, error("%s: Zerocoinspend does not have the correct denomination", __func__));

//check that denomination is what it claims to be in nSequence
if (newSpend.getDenomination() != txin.nSequence)
return state.DoS(100, error("Zerocoinspend nSequence denomination does not match CoinSpend"));
return state.DoS(100, error("%s: Zerocoinspend nSequence denomination does not match CoinSpend", __func__));

//make sure the txout has not changed
if (newSpend.getTxOutHash() != hashTxOut)
return state.DoS(100, error("Zerocoinspend does not use the same txout that was used in the SoK"));
return state.DoS(100, error("%s: Zerocoinspend does not use the same txout that was used in the SoK", __func__));

if (isPublicSpend) {
libzerocoin::ZerocoinParams* params = consensus.Zerocoin_Params(false);
PublicCoinSpend ret(params);
if (!ZPIVModule::validateInput(txin, prevOut, tx, ret)){
return state.DoS(100, error("CheckZerocoinSpend(): public zerocoin spend did not verify"));
return state.DoS(100, error("%s: public zerocoin spend did not verify", __func__));
}
}

if (serials.count(newSpend.getCoinSerialNumber()))
return state.DoS(100, error("Zerocoinspend serial is used twice in the same tx"));
return state.DoS(100, error("%s: Zerocoinspend serial is used twice in the same tx", __func__));
serials.insert(newSpend.getCoinSerialNumber());

//make sure that there is no over redemption of coins
Expand All @@ -96,8 +97,8 @@ bool CheckZerocoinSpend(const CTransaction& tx, bool fVerifySignature, CValidati
}

if (!tx.IsCoinStake() && nTotalRedeemed < tx.GetValueOut()) {
LogPrintf("redeemed = %s , spend = %s \n", FormatMoney(nTotalRedeemed), FormatMoney(tx.GetValueOut()));
return state.DoS(100, error("Transaction spend more than was redeemed in zerocoins"));
LogPrintf("%s: redeemed = %s , spend = %s \n", __func__, FormatMoney(nTotalRedeemed), FormatMoney(tx.GetValueOut()));
return state.DoS(100, error("%s: Transaction spend more than was redeemed in zerocoins", __func__));
}

return fValidated;
Expand Down Expand Up @@ -135,6 +136,65 @@ bool CheckPublicCoinSpendVersion(int version) {
return version == CurrentPublicCoinSpendVersion();
}

bool ContextualCheckZerocoinTx(const CTransactionRef& tx, CValidationState& state, const Consensus::Params& consensus, int nHeight)
{
// zerocoin enforced via block time. First block with a zc mint is 863735
const bool fZerocoinEnforced = (nHeight >= consensus.ZC_HeightStart);
const bool fPublicSpendEnforced = consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_ZC_PUBLIC);
const bool fRejectMintsAndPrivateSpends = !fZerocoinEnforced || fPublicSpendEnforced;
const bool fRejectPublicSpends = !fPublicSpendEnforced || consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V5_0);

const bool hasPrivateSpendInputs = !tx->vin.empty() && tx->vin[0].IsZerocoinSpend();
const bool hasPublicSpendInputs = !tx->vin.empty() && tx->vin[0].IsZerocoinPublicSpend();
const std::string& txId = tx->GetHash().ToString();

int nSpendCount{0};
for (const CTxIn& in : tx->vin) {
if (in.IsZerocoinSpend()) {
if (fRejectMintsAndPrivateSpends)
return state.DoS(100, error("%s: zerocoin spend tx %s not accepted at height %d",
__func__, txId, nHeight), REJECT_INVALID, "bad-txns-zc-private-spend");
if (!hasPrivateSpendInputs)
return state.DoS(100, error("%s: zerocoin spend tx %s has mixed spend inputs",
__func__, txId), REJECT_INVALID, "bad-txns-zc-private-spend-mixed-types");
if (++nSpendCount > consensus.ZC_MaxSpendsPerTx)
return state.DoS(100, error("%s: zerocoin spend tx %s has more than %d inputs",
__func__, txId, consensus.ZC_MaxSpendsPerTx), REJECT_INVALID, "bad-txns-zc-private-spend-max-inputs");

} else if (in.IsZerocoinPublicSpend()) {
if (fRejectPublicSpends)
return state.DoS(100, error("%s: zerocoin public spend tx %s not accepted at height %d",
__func__, txId, nHeight), REJECT_INVALID, "bad-txns-zc-public-spend");
if (!hasPublicSpendInputs)
return state.DoS(100, error("%s: zerocoin spend tx %s has mixed spend inputs",
__func__, txId), REJECT_INVALID, "bad-txns-zc-public-spend-mixed-types");
if (++nSpendCount > consensus.ZC_MaxPublicSpendsPerTx)
return state.DoS(100, error("%s: zerocoin spend tx %s has more than %d inputs",
__func__, txId, consensus.ZC_MaxPublicSpendsPerTx), REJECT_INVALID, "bad-txns-zc-public-spend-max-inputs");

} else {
// this is a transparent input
if (hasPrivateSpendInputs || hasPublicSpendInputs)
return state.DoS(100, error("%s: zerocoin spend tx %s has mixed spend inputs",
__func__, txId), REJECT_INVALID, "bad-txns-zc-spend-mixed-types");
}
}

if (hasPrivateSpendInputs || hasPublicSpendInputs) {
if (!CheckZerocoinSpend(tx, state))
return false; // failure reason logged in validation state
}

for (const CTxOut& o : tx->vout) {
if (o.IsZerocoinMint() && fRejectMintsAndPrivateSpends) {
return state.DoS(100, error("%s: zerocoin mint tx %s not accepted at height %d",
__func__, txId, nHeight), REJECT_INVALID, "bad-txns-zc-mint");
}
}

return true;
}

bool ContextualCheckZerocoinSpend(const CTransaction& tx, const libzerocoin::CoinSpend* spend, int nHeight, const uint256& hashBlock)
{
if(!ContextualCheckZerocoinSpendNoSerialCheck(tx, spend, nHeight, hashBlock)){
Expand Down
3 changes: 1 addition & 2 deletions src/consensus/zerocoin_verify.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@
#include "script/interpreter.h"
#include "zpivchain.h"

/** Context-independent validity checks */
bool CheckZerocoinSpend(const CTransaction& tx, bool fVerifySignature, CValidationState& state, bool fFakeSerialAttack = false);
// Fake Serial attack Range
bool isBlockBetweenFakeSerialAttackRange(int nHeight);
// Public coin spend
bool CheckPublicCoinSpendEnforced(int blockHeight, bool isPublicSpend);
int CurrentPublicCoinSpendVersion();
bool CheckPublicCoinSpendVersion(int version);
bool ContextualCheckZerocoinTx(const CTransactionRef& tx, CValidationState& state, const Consensus::Params& consensus, int nHeight);
bool ContextualCheckZerocoinSpend(const CTransaction& tx, const libzerocoin::CoinSpend* spend, int nHeight, const uint256& hashBlock);
bool ContextualCheckZerocoinSpendNoSerialCheck(const CTransaction& tx, const libzerocoin::CoinSpend* spend, int nHeight, const uint256& hashBlock);

Expand Down
Loading