#include "consensus/validation.h"
#include "init.h"
#include "merkleblock.h"
+#include "metrics.h"
#include "net.h"
#include "pow.h"
#include "txdb.h"
#include "util.h"
#include "utilmoneystr.h"
#include "validationinterface.h"
+#include "wallet/asyncrpcoperation_sendmany.h"
#include <sstream>
using namespace std;
#if defined(NDEBUG)
-# error "Bitcoin cannot be compiled without assertions."
+# error "Zcash cannot be compiled without assertions."
#endif
/**
CWaitableCriticalSection csBestBlock;
CConditionVariable cvBlockChange;
int nScriptCheckThreads = 0;
+bool fExperimentalMode = false;
bool fImporting = false;
bool fReindex = false;
bool fTxIndex = false;
bool fAlerts = DEFAULT_ALERTS;
/** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */
-CFeeRate minRelayTxFee = CFeeRate(5000);
+CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CTxMemPool mempool(::minRelayTxFee);
/**
* Check transaction inputs to mitigate two
* potential denial-of-service attacks:
- *
+ *
* 1. scriptSigs with extra data stuffed into them,
* not consumed by scriptPubKey (or P2SH script)
* 2. P2SH scripts with a crazy number of expensive
return nSigOps;
}
-bool CheckTransaction(const CTransaction& tx, CValidationState &state)
+bool CheckTransaction(const CTransaction& tx, CValidationState &state,
+ libzcash::ProofVerifier& verifier)
{
+ // Don't count coinbase transactions because mining skews the count
+ if (!tx.IsCoinBase()) {
+ transactionsValidated.increment();
+ }
+
if (!CheckTransactionWithoutProofVerification(tx, state)) {
return false;
} else {
// Ensure that zk-SNARKs verify
BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) {
- if (!joinsplit.Verify(*pzcashParams, tx.joinSplitPubKey)) {
+ if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) {
return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"),
REJECT_INVALID, "bad-txns-joinsplit-verification-failed");
}
{
// Basic checks that don't depend on any context
+ // Check transaction version
+ if (tx.nVersion < MIN_TX_VERSION) {
+ return state.DoS(100, error("CheckTransaction(): version too low"),
+ REJECT_INVALID, "bad-txns-version-too-low");
+ }
+
// Transactions can contain empty `vin` and `vout` so long as
// `vjoinsplit` is non-empty.
if (tx.vin.empty() && tx.vjoinsplit.empty())
if (pfMissingInputs)
*pfMissingInputs = false;
- if (!CheckTransaction(tx, state))
+ auto verifier = libzcash::ProofVerifier::Strict();
+ if (!CheckTransaction(tx, state, verifier))
return error("AcceptToMemoryPool: CheckTransaction failed");
// Coinbase is only valid in a block, not as a loose transaction
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx));
unsigned int nSize = entry.GetTxSize();
- // Don't accept it if it can't get into a block
- CAmount txMinFee = GetMinRelayFee(tx, nSize, true);
- if (fLimitFree && nFees < txMinFee)
- return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",
- hash.ToString(), nFees, txMinFee),
- REJECT_INSUFFICIENTFEE, "insufficient fee");
+ // Accept a tx if it contains joinsplits and has at least the default fee specified by z_sendmany.
+ if (tx.vjoinsplit.size() > 0 && nFees >= ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE) {
+ // In future we will we have more accurate and dynamic computation of fees for tx with joinsplits.
+ } else {
+ // Don't accept it if it can't get into a block
+ CAmount txMinFee = GetMinRelayFee(tx, nSize, true);
+ if (fLimitFree && nFees < txMinFee)
+ return state.DoS(0, error("AcceptToMemoryPool: not enough fees %s, %d < %d",
+ hash.ToString(), nFees, txMinFee),
+ REJECT_INSUFFICIENTFEE, "insufficient fee");
+ }
// Require that free transactions have sufficient priority to be mined in the next block.
if (GetBoolArg("-relaypriority", false) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(view.GetPriority(tx, chainActive.Height() + 1))) {
}
// We define a condition where we should warn the user about as a fork of at least 7 blocks
- // with a tip within 72 blocks (+/- 12 hours if no one mines it) of ours
+ // with a tip within 72 blocks (+/- 3 hours if no one mines it) of ours
// We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network
// hash rate operating on the fork.
// or a chain that is entirely longer than ours and invalid (note that this should be detected by both)
static CCheckQueue<CScriptCheck> scriptcheckqueue(128);
void ThreadScriptCheck() {
- RenameThread("bitcoin-scriptch");
+ RenameThread("zcash-scriptch");
scriptcheckqueue.Thread();
}
{
const CChainParams& chainparams = Params();
AssertLockHeld(cs_main);
- // Check it again in case a previous version let a bad block in
- if (!CheckBlock(block, state, !fJustCheck, !fJustCheck))
+
+ bool fExpensiveChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()));
+ auto verifier = libzcash::ProofVerifier::Strict();
+ auto disabledVerifier = libzcash::ProofVerifier::Disabled();
+
+ // Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in
+ if (!CheckBlock(block, state, fExpensiveChecks ? verifier : disabledVerifier, !fJustCheck, !fJustCheck))
return false;
// verify that the view's current state corresponds to the previous block
return true;
}
- bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints()));
-
// Do not allow blocks that contain transactions which 'overwrite' older transactions,
// unless those are already completely spent.
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
CBlockUndo blockundo;
- CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
+ CCheckQueueControl<CScriptCheck> control(fExpensiveChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
int64_t nTimeStart = GetTimeMicros();
CAmount nFees = 0;
nFees += view.GetValueIn(tx)-tx.GetValueOut();
std::vector<CScriptCheck> vChecks;
- if (!ContextualCheckInputs(tx, state, view, fScriptChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL))
+ if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL))
return false;
control.Add(vChecks);
}
static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0;
-/**
+/**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk.
*/
pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
}
// Notify external listeners about the new tip.
+ GetMainSignals().UpdatedBlockTip(pindexNewTip);
uiInterface.NotifyBlockTip(hashNewTip);
}
} while(pindexMostWork != chainActive.Tip());
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW)
{
+ // Check block version
+ if (block.nVersion < MIN_BLOCK_VERSION)
+ return state.DoS(100, error("CheckBlockHeader(): block version too low"),
+ REJECT_INVALID, "version-too-low");
+
// Check Equihash solution is valid
if (fCheckPOW && !CheckEquihashSolution(&block, Params()))
return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),
return true;
}
-bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot)
+bool CheckBlock(const CBlock& block, CValidationState& state,
+ libzcash::ProofVerifier& verifier,
+ bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context.
// Check transactions
BOOST_FOREACH(const CTransaction& tx, block.vtx)
- if (!CheckTransaction(tx, state))
+ if (!CheckTransaction(tx, state, verifier))
return error("CheckBlock(): CheckTransaction failed");
unsigned int nSigOps = 0;
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
- if (block.nVersion >= 2)
+ // Since MIN_BLOCK_VERSION = 4 all blocks with nHeight > 0 should satisfy this.
+ // This rule is not applied to the genesis block, which didn't include the height
+ // in the coinbase.
+ if (nHeight > 0)
{
CScript expect = CScript() << nHeight;
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() ||
if (fTooFarAhead) return true; // Block height is too high
}
- if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) {
+ // See method docstring for why this is always disabled
+ auto verifier = libzcash::ProofVerifier::Disabled();
+ if ((!CheckBlock(block, state, verifier)) || !ContextualCheckBlock(block, state, pindex->pprev)) {
if (state.IsInvalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
setDirtyBlockIndex.insert(pindex);
bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp)
{
// Preliminary checks
- bool checked = CheckBlock(*pblock, state);
+ auto verifier = libzcash::ProofVerifier::Disabled();
+ bool checked = CheckBlock(*pblock, state, verifier);
{
LOCK(cs_main);
CBlockIndex indexDummy(block);
indexDummy.pprev = pindexPrev;
indexDummy.nHeight = pindexPrev->nHeight + 1;
+ // JoinSplit proofs are verified in ConnectBlock
+ auto verifier = libzcash::ProofVerifier::Disabled();
// NOTE: CheckBlockHeader is called by CheckBlock
if (!ContextualCheckBlockHeader(block, state, pindexPrev))
return false;
- if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot))
+ if (!CheckBlock(block, state, verifier, fCheckPOW, fCheckMerkleRoot))
return false;
if (!ContextualCheckBlock(block, state, pindexPrev))
return false;
CBlockIndex* pindexFailure = NULL;
int nGoodTransactions = 0;
CValidationState state;
+ // No need to verify JoinSplits twice
+ auto verifier = libzcash::ProofVerifier::Disabled();
for (CBlockIndex* pindex = chainActive.Tip(); pindex && pindex->pprev; pindex = pindex->pprev)
{
boost::this_thread::interruption_point();
if (!ReadBlockFromDisk(block, pindex))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 1: verify block validity
- if (nCheckLevel >= 1 && !CheckBlock(block, state))
+ if (nCheckLevel >= 1 && !CheckBlock(block, state, verifier))
return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
// check level 2: verify undo validity
if (nCheckLevel >= 2 && pindex) {
// CAlert
//
-string GetWarnings(string strFor)
+std::string GetWarnings(const std::string& strFor)
{
int nPriority = 0;
string strStatusBar;
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived)
{
const CChainParams& chainparams = Params();
- RandAddSeedPerfmon();
LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id);
if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0)
{
// the getaddr message mitigates the attack.
else if ((strCommand == "getaddr") && (pfrom->fInbound))
{
+ // Only send one GetAddr response per connection to reduce resource waste
+ // and discourage addr stamping of INV announcements.
+ if (pfrom->fSentAddr) {
+ LogPrint("net", "Ignoring repeated \"getaddr\". peer=%d\n", pfrom->id);
+ return true;
+ }
+ pfrom->fSentAddr = true;
+
pfrom->vAddrToSend.clear();
vector<CAddress> vAddr = addrman.GetAddr();
BOOST_FOREACH(const CAddress &addr, vAddr)
}
}
- else
- {
+ else if (strCommand == "notfound") {
+ // We do not care about the NOTFOUND message, but logging an Unknown Command
+ // message would be undesirable as we transmit it ourselves.
+ }
+
+ else {
// Ignore unknown commands for extensibility
LogPrint("net", "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->id);
}