X-Git-Url: https://repo.jachan.dev/VerusCoin.git/blobdiff_plain/c86a1cb86ed2d850946ba17d8736cc511b317c71..eb9facd492d9c60055ad1f159b1b0e9fc8fda371:/src/main.cpp diff --git a/src/main.cpp b/src/main.cpp index b43bbd9ef..6e85b208a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,11 +46,13 @@ using namespace std; # error "Zcash cannot be compiled without assertions." #endif + /** * Global state */ CCriticalSection cs_main; +extern uint8_t NOTARY_PUBKEY33[33]; BlockMap mapBlockIndex; CChain chainActive; @@ -73,6 +75,8 @@ size_t nCoinCacheUsage = 5000 * 300; uint64_t nPruneTarget = 0; bool fAlerts = DEFAULT_ALERTS; +unsigned int expiryDelta = DEFAULT_TX_EXPIRY_DELTA; + /** Fees smaller than this (in satoshi) are considered zero fee (for relaying and mining) */ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); @@ -96,7 +100,7 @@ static void CheckBlockIndex(); /** Constant stuff for coinbase transactions we create: */ CScript COINBASE_FLAGS; -const string strMessageMagic = "Zcash Signed Message:\n"; +const string strMessageMagic = "Komodo Signed Message:\n"; // Internal stuff namespace { @@ -328,7 +332,19 @@ void FinalizeNode(NodeId nodeid) { mapNodeState.erase(nodeid); } - + +void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) +{ +/* int expired = pool.Expire(GetTime() - age); + if (expired != 0) + LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired); + + std::vector<uint256> vNoSpendsRemaining; + pool.TrimToSize(limit, &vNoSpendsRemaining); + BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining) + pcoinsTip->Uncache(removed);*/ +} + // Requires cs_main. // Returns a bool indicating whether we requested this block. bool MarkBlockAsReceived(const uint256& hash) { @@ -370,7 +386,8 @@ void ProcessBlockAvailability(NodeId nodeid) { if (!state->hashLastUnknownBlock.IsNull()) { BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock); - if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) { + if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) + { if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) state->pindexBestKnownBlock = itOld->second; state->hashLastUnknownBlock.SetNull(); @@ -383,14 +400,15 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { CNodeState *state = State(nodeid); assert(state != NULL); - ProcessBlockAvailability(nodeid); + /*ProcessBlockAvailability(nodeid); BlockMap::iterator it = mapBlockIndex.find(hash); if (it != mapBlockIndex.end() && it->second->nChainWork > 0) { // An actually better block was announced. if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork) state->pindexBestKnownBlock = it->second; - } else { + } else*/ + { // An unknown block was announced; just assume that the latest one is the best one. state->hashLastUnknownBlock = hash; } @@ -466,7 +484,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBl } // Iterate over those blocks in vToFetch (in forward direction), adding the ones that - // are not yet downloaded and not in flight to vBlocks. In the mean time, update + // are not yet downloaded and not in flight to vBlocks. In the meantime, update // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's // already part of our chain (and therefore don't need it even if pruned). BOOST_FOREACH(CBlockIndex* pindex, vToFetch) { @@ -542,7 +560,7 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc if (mi != mapBlockIndex.end()) { CBlockIndex* pindex = (*mi).second; - if (chain.Contains(pindex)) + if (pindex != 0 && chain.Contains(pindex)) return pindex; if (pindex->GetAncestor(chain.Height()) == chain.Tip()) { return chain.Tip(); @@ -555,6 +573,11 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc CCoinsViewCache *pcoinsTip = NULL; CBlockTreeDB *pblocktree = NULL; +// Komodo globals + +#define KOMODO_ZCASH +#include "komodo.h" + ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions @@ -678,16 +701,22 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) } } - unsigned int nDataOut = 0; + unsigned int v=0,nDataOut = 0; txnouttype whichType; - BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey, whichType)) { + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + if (!::IsStandard(txout.scriptPubKey, whichType)) + { reason = "scriptpubkey"; + fprintf(stderr,">>>>>>>>>>>>>>> vout.%d nDataout.%d\n",v,nDataOut); return false; } - + if (whichType == TX_NULL_DATA) + { nDataOut++; + //fprintf(stderr,"is OP_RETURN\n"); + } else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { reason = "bare-multisig"; return false; @@ -695,6 +724,7 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) reason = "dust"; return false; } + v++; } // only one OP_RETURN txout is permitted @@ -708,16 +738,34 @@ bool IsStandardTx(const CTransaction& tx, string& reason, const int nHeight) bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { + int32_t i; if (tx.nLockTime == 0) return true; if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) return true; BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (!txin.IsFinal()) + { + if ( txin.nSequence == 0xfffffffe && (((int64_t)tx.nLockTime >= LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime > nBlockTime) || ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD && (int64_t)tx.nLockTime > nBlockHeight)) ) + { + + } + else if (!txin.IsFinal()) + { + printf("non-final txin seq.%x locktime.%u vs nTime.%u\n",txin.nSequence,(uint32_t)tx.nLockTime,(uint32_t)nBlockTime); return false; + } + } return true; } +bool IsExpiredTx(const CTransaction &tx, int nBlockHeight) +{ + if (tx.nExpiryHeight == 0 || tx.IsCoinBase()) { + return false; + } + return static_cast<uint32_t>(nBlockHeight) > tx.nExpiryHeight; +} + bool CheckFinalTx(const CTransaction &tx, int flags) { AssertLockHeld(cs_main); @@ -758,7 +806,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) * 2. P2SH scripts with a crazy number of expensive * CHECKSIG/CHECKMULTISIG operations */ -bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) +bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, uint32_t consensusBranchId) { if (tx.IsCoinBase()) return true; // Coinbases don't use vin normally @@ -784,7 +832,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) // IsStandardTx() will have already returned false // and this method isn't called. vector<vector<unsigned char> > stack; - if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SIGVERSION_BASE)) + if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), consensusBranchId)) return false; if (whichType == TX_SCRIPTHASH) @@ -884,16 +932,61 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, return state.DoS(dosLevel, error("ContextualCheckTransaction: overwinter is active"), REJECT_INVALID, "tx-overwinter-active"); } + + // Check that all transactions are unexpired + if (IsExpiredTx(tx, nHeight)) { + return state.DoS(dosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired"); + } } + if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) { + auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus()); + // Empty output script. + CScript scriptCode; + uint256 dataToBeSigned; + try { + dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); + } catch (std::logic_error ex) { + return state.DoS(100, error("CheckTransaction(): error computing signature hash"), + REJECT_INVALID, "error-computing-signature-hash"); + } + + BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32); + + // We rely on libsodium to check that the signature is canonical. + // https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0 + if (crypto_sign_verify_detached(&tx.joinSplitSig[0], + dataToBeSigned.begin(), 32, + tx.joinSplitPubKey.begin() + ) != 0) { + return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"), + REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); + } + } return true; } - bool CheckTransaction(const CTransaction& tx, CValidationState &state, libzcash::ProofVerifier& verifier) { - // Don't count coinbase transactions because mining skews the count + static uint256 array[64]; static int32_t numbanned,indallvouts; int32_t j,k,n; + if ( *(int32_t *)&array[0] == 0 ) + numbanned = komodo_bannedset(&indallvouts,array,(int32_t)(sizeof(array)/sizeof(*array))); + n = tx.vin.size(); + for (j=0; j<n; j++) + { + for (k=0; k<numbanned; k++) + { + if ( tx.vin[j].prevout.hash == array[k] && (tx.vin[j].prevout.n == 1 || k >= indallvouts) ) + { + static uint32_t counter; + if ( counter++ < 100 ) + printf("MEMPOOL: banned tx.%d being used at ht.%d vout.%d\n",k,(int32_t)chainActive.Tip()->nHeight,j); + return(false); + } + } + } + // Don't count coinbase transactions because mining skews the count if (!tx.IsCoinBase()) { transactionsValidated.increment(); } @@ -980,8 +1073,10 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio return state.DoS(100, error("CheckTransaction(): txout.nValue negative"), REJECT_INVALID, "bad-txns-vout-negative"); if (txout.nValue > MAX_MONEY) - return state.DoS(100, error("CheckTransaction(): txout.nValue too high"), - REJECT_INVALID, "bad-txns-vout-toolarge"); + { + fprintf(stderr,"%.8f > max %.8f\n",(double)txout.nValue/COIN,(double)MAX_MONEY/COIN); + return state.DoS(100, error("CheckTransaction(): txout.nValue too high"),REJECT_INVALID, "bad-txns-vout-toolarge"); + } nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) return state.DoS(100, error("CheckTransaction(): txout total out of range"), @@ -1082,30 +1177,6 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio if (txin.prevout.IsNull()) return state.DoS(10, error("CheckTransaction(): prevout is null"), REJECT_INVALID, "bad-txns-prevout-null"); - - if (tx.vjoinsplit.size() > 0) { - // Empty output script. - CScript scriptCode; - uint256 dataToBeSigned; - try { - dataToBeSigned = SignatureHash(scriptCode, tx, NOT_AN_INPUT, SIGHASH_ALL, 0, SIGVERSION_BASE); - } catch (std::logic_error ex) { - return state.DoS(100, error("CheckTransaction(): error computing signature hash"), - REJECT_INVALID, "error-computing-signature-hash"); - } - - BOOST_STATIC_ASSERT(crypto_sign_PUBLICKEYBYTES == 32); - - // We rely on libsodium to check that the signature is canonical. - // https://github.com/jedisct1/libsodium/commit/62911edb7ff2275cccd74bf1c8aefcc4d76924e0 - if (crypto_sign_verify_detached(&tx.joinSplitSig[0], - dataToBeSigned.begin(), 32, - tx.joinSplitPubKey.begin() - ) != 0) { - return state.DoS(100, error("CheckTransaction(): invalid joinsplit signature"), - REJECT_INVALID, "bad-txns-invalid-joinsplit-signature"); - } - } } return true; @@ -1113,6 +1184,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree) { + extern int32_t KOMODO_ON_DEMAND; { LOCK(mempool.cs); uint256 hash = tx.GetHash(); @@ -1141,13 +1213,15 @@ CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF } -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, - bool* pfMissingInputs, bool fRejectAbsurdFee) +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,bool* pfMissingInputs, bool fRejectAbsurdFee) { AssertLockHeld(cs_main); if (pfMissingInputs) *pfMissingInputs = false; - + + int nextBlockHeight = chainActive.Height() + 1; + auto consensusBranchId = CurrentEpochBranchId(nextBlockHeight, Params().GetConsensus()); + // Node operator can choose to reject tx by number of transparent inputs static_assert(std::numeric_limits<size_t>::max() >= std::numeric_limits<int64_t>::max(), "size_t too small"); size_t limit = (size_t) GetArg("-mempooltxinputlimit", 0); @@ -1158,111 +1232,137 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa return false; } } - + auto verifier = libzcash::ProofVerifier::Strict(); + if ( komodo_validate_interest(tx,chainActive.Tip()->nHeight+1,chainActive.Tip()->GetMedianTimePast() + 777,0) < 0 ) + { + //fprintf(stderr,"AcceptToMemoryPool komodo_validate_interest failure\n"); + return error("AcceptToMemoryPool: komodo_validate_interest failed"); + } if (!CheckTransaction(tx, state, verifier)) return error("AcceptToMemoryPool: CheckTransaction failed"); - + // DoS level set to 10 to be more forgiving. // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. - int nextBlockHeight = chainActive.Height() + 1; if (!ContextualCheckTransaction(tx, state, nextBlockHeight, 10)) { return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); } - + // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"), - REJECT_INVALID, "coinbase"); - + { + fprintf(stderr,"AcceptToMemoryPool coinbase as individual tx\n"); + return state.DoS(100, error("AcceptToMemoryPool: coinbase as individual tx"),REJECT_INVALID, "coinbase"); + } // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (Params().RequireStandard() && !IsStandardTx(tx, reason, nextBlockHeight)) - return state.DoS(0, - error("AcceptToMemoryPool: nonstandard transaction: %s", reason), - REJECT_NONSTANDARD, reason); - + { + fprintf(stderr,"AcceptToMemoryPool reject nonstandard transaction: %s\n",reason.c_str()); + return state.DoS(0,error("AcceptToMemoryPool: nonstandard transaction: %s", reason),REJECT_NONSTANDARD, reason); + } // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) + { + //fprintf(stderr,"AcceptToMemoryPool reject non-final\n"); return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); - + } // is it already in the memory pool? uint256 hash = tx.GetHash(); if (pool.exists(hash)) + { + fprintf(stderr,"already in mempool\n"); return false; - + } + // Check for conflicts with in-memory transactions { - LOCK(pool.cs); // protect pool.mapNextTx - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - COutPoint outpoint = tx.vin[i].prevout; - if (pool.mapNextTx.count(outpoint)) + LOCK(pool.cs); // protect pool.mapNextTx + for (unsigned int i = 0; i < tx.vin.size(); i++) { - // Disable replacement feature for now - return false; - } - } - BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { - BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { - if (pool.mapNullifiers.count(nf)) + COutPoint outpoint = tx.vin[i].prevout; + if (pool.mapNextTx.count(outpoint)) { + static uint32_t counter; + // Disable replacement feature for now + //if ( counter++ < 100 ) + fprintf(stderr,"Disable replacement feature for now\n"); return false; } } - } + BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) + { + BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) + { + if (pool.mapNullifiers.count(nf)) + { + fprintf(stderr,"pool.mapNullifiers.count\n"); + return false; + } + } + } } { CCoinsView dummy; CCoinsViewCache view(&dummy); - + int64_t interest; CAmount nValueIn = 0; { - LOCK(pool.cs); - CCoinsViewMemPool viewMemPool(pcoinsTip, pool); - view.SetBackend(viewMemPool); - - // do we already have it? - if (view.HaveCoins(hash)) - return false; - - // do all inputs exist? - // Note that this does not check for the presence of actual outputs (see the next check for that), - // and only helps with filling in pfMissingInputs (to determine missing vs spent). - BOOST_FOREACH(const CTxIn txin, tx.vin) { - if (!view.HaveCoins(txin.prevout.hash)) { - if (pfMissingInputs) - *pfMissingInputs = true; + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(pcoinsTip, pool); + view.SetBackend(viewMemPool); + + // do we already have it? + if (view.HaveCoins(hash)) + { + fprintf(stderr,"view.HaveCoins(hash) error\n"); return false; } - } - - // are the actual inputs available? - if (!view.HaveInputs(tx)) - return state.Invalid(error("AcceptToMemoryPool: inputs already spent"), - REJECT_DUPLICATE, "bad-txns-inputs-spent"); - - // are the joinsplit's requirements met? - if (!view.HaveJoinSplitRequirements(tx)) - return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"), - REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); - - // Bring the best block into scope - view.GetBestBlock(); - - nValueIn = view.GetValueIn(tx); - - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool - view.SetBackend(dummy); - } - + + // do all inputs exist? + // Note that this does not check for the presence of actual outputs (see the next check for that), + // and only helps with filling in pfMissingInputs (to determine missing vs spent). + BOOST_FOREACH(const CTxIn txin, tx.vin) + { + if (!view.HaveCoins(txin.prevout.hash)) + { + if (pfMissingInputs) + *pfMissingInputs = true; + //fprintf(stderr,"missing inputs\n"); + return false; + } + } + + // are the actual inputs available? + if (!view.HaveInputs(tx)) + { + //fprintf(stderr,"accept failure.1\n"); + return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent"); + } + // are the joinsplit's requirements met? + if (!view.HaveJoinSplitRequirements(tx)) + { + fprintf(stderr,"accept failure.2\n"); + return state.Invalid(error("AcceptToMemoryPool: joinsplit requirements not met"),REJECT_DUPLICATE, "bad-txns-joinsplit-requirements-not-met"); + } + + // Bring the best block into scope + view.GetBestBlock(); + + nValueIn = view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime); + if ( 0 && interest != 0 ) + fprintf(stderr,"add interest %.8f\n",(double)interest/COIN); + // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool + view.SetBackend(dummy); + } + // Check for non-standard pay-to-script-hash in inputs - if (Params().RequireStandard() && !AreInputsStandard(tx, view)) - return error("AcceptToMemoryPool: nonstandard transaction input"); - + if (Params().RequireStandard() && !AreInputsStandard(tx, view, consensusBranchId)) + return error("AcceptToMemoryPool: reject nonstandard transaction input"); + // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction // itself can contain sigops MAX_STANDARD_TX_SIGOPS is less than @@ -1271,15 +1371,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa unsigned int nSigOps = GetLegacySigOpCount(tx); nSigOps += GetP2SHSigOpCount(tx, view); if (nSigOps > MAX_STANDARD_TX_SIGOPS) - return state.DoS(0, - error("AcceptToMemoryPool: too many sigops %s, %d > %d", - hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS), - REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); + { + fprintf(stderr,"accept failure.4\n"); + return state.DoS(0, error("AcceptToMemoryPool: too many sigops %s, %d > %d", hash.ToString(), nSigOps, MAX_STANDARD_TX_SIGOPS),REJECT_NONSTANDARD, "bad-txns-too-many-sigops"); + } CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); - + // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; @@ -1290,15 +1390,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa break; } } - + // Grab the branch ID we expect this transaction to commit to. We don't // yet know if it does, but if the entry gets added to the mempool, then // it has passed ContextualCheckInputs and therefore this is correct. auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); - + CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), mempool.HasNoInputsOf(tx), fSpendsCoinbase, consensusBranchId); unsigned int nSize = entry.GetTxSize(); - + // 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. @@ -1306,16 +1406,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // 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"); + { + fprintf(stderr,"accept failure.5\n"); + 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))) { + fprintf(stderr,"accept failure.6\n"); return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority"); } - + // Continuously rate-limit free (really, very-low-fee) transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. @@ -1325,33 +1427,38 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa static double dFreeCount; static int64_t nLastTime; int64_t nNow = GetTime(); - + LOCK(csFreeLimiter); - + // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); nLastTime = nNow; // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), - REJECT_INSUFFICIENTFEE, "rate limited free transaction"); + { + fprintf(stderr,"accept failure.7\n"); + return state.DoS(0, error("AcceptToMemoryPool: free transaction rejected by rate limiter"), REJECT_INSUFFICIENTFEE, "rate limited free transaction"); + } LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } - - if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000) - return error("AcceptToMemoryPool: absurdly high fees %s, %d > %d", - hash.ToString(), - nFees, ::minRelayTxFee.GetFee(nSize) * 10000); - + + if (fRejectAbsurdFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000 && nFees > nValueOut/19 ) + { + fprintf(stderr,"accept failure.8\n"); + return error("AcceptToMemoryPool: absurdly high fees %s, %d > %d",hash.ToString(), nFees, ::minRelayTxFee.GetFee(nSize) * 10000); + } + // Check against previous transactions // This is done last to help prevent CPU exhaustion denial-of-service attacks. - if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, Params().GetConsensus())) + PrecomputedTransactionData txdata(tx); + if (!ContextualCheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { + fprintf(stderr,"accept failure.9\n"); return error("AcceptToMemoryPool: ConnectInputs failed %s", hash.ToString()); } - + // Check again against just the consensus-critical mandatory script // verification flags, in case of bugs in the standard flags that cause // transactions to pass as valid when they're actually invalid. For @@ -1361,17 +1468,20 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // There is a similar check in CreateNewBlock() to prevent creating // invalid blocks, however allowing such transactions into the mempool // can be exploited as a DoS attack. - if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, Params().GetConsensus())) + if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { + fprintf(stderr,"accept failure.10\n"); return error("AcceptToMemoryPool: BUG! PLEASE REPORT THIS! ConnectInputs failed against MANDATORY but not STANDARD flags %s", hash.ToString()); } - + // Store transaction in memory + if ( komodo_is_notarytx(tx) == 0 ) + KOMODO_ON_DEMAND++; pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); } - + SyncWithWallets(tx, NULL); - + return true; } @@ -1436,9 +1546,19 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock return false; } - - - +/*char *komodo_getspendscript(uint256 hash,int32_t n) +{ + CTransaction tx; uint256 hashBlock; + if ( !GetTransaction(hash,tx,hashBlock,true) ) + { + printf("null GetTransaction\n"); + return(0); + } + if ( n >= 0 && n < tx.vout.size() ) + return((char *)tx.vout[n].scriptPubKey.ToString().c_str()); + else printf("getspendscript illegal n.%d\n",n); + return(0); +}*/ ////////////////////////////////////////////////////////////////////////////// @@ -1467,34 +1587,45 @@ bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos, const CMessageHeader::M return true; } -bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) +bool ReadBlockFromDisk(int32_t height,CBlock& block, const CDiskBlockPos& pos) { + uint8_t pubkey33[33]; block.SetNull(); // Open history file to read CAutoFile filein(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); if (filein.IsNull()) - return error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); + { + //fprintf(stderr,"readblockfromdisk err A\n"); + return false;//error("ReadBlockFromDisk: OpenBlockFile failed for %s", pos.ToString()); + } // Read block try { filein >> block; } catch (const std::exception& e) { + fprintf(stderr,"readblockfromdisk err B\n"); return error("%s: Deserialize or I/O error - %s at %s", __func__, e.what(), pos.ToString()); } - // Check the header - if (!(CheckEquihashSolution(&block, Params()) && - CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus()))) + komodo_block2pubkey33(pubkey33,block); + if (!(CheckEquihashSolution(&block, Params()) && CheckProofOfWork(height,pubkey33,block.GetHash(), block.nBits, Params().GetConsensus()))) + { + int32_t i; for (i=0; i<33; i++) + printf("%02x",pubkey33[i]); + fprintf(stderr," warning unexpected diff at ht.%d\n",height); + return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString()); - + } return true; } bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) { - if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) + if ( pindex == 0 ) + return false; + if (!ReadBlockFromDisk(pindex->nHeight,block, pindex->GetBlockPos())) return false; if (block.GetHash() != pindex->GetBlockHash()) return error("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s", @@ -1502,10 +1633,68 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) return true; } +//uint64_t komodo_moneysupply(int32_t height); +extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +extern uint32_t ASSETCHAINS_MAGIC; +extern uint64_t ASSETCHAINS_ENDSUBSIDY,ASSETCHAINS_REWARD,ASSETCHAINS_HALVING,ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY; + CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { - CAmount nSubsidy = 12.5 * COIN; - + static uint64_t cached_subsidy; static int32_t cached_numhalvings; + int32_t numhalvings,i; uint64_t numerator; CAmount nSubsidy = 3 * COIN; + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + { + if ( nHeight == 1 ) + return(100000000 * COIN); // ICO allocation + else if ( nHeight < KOMODO_ENDOFERA ) //komodo_moneysupply(nHeight) < MAX_MONEY ) + return(3 * COIN); + else return(0); + } + else + { + if ( nHeight == 1 ) + return(ASSETCHAINS_SUPPLY * COIN + (ASSETCHAINS_MAGIC & 0xffffff)); + else if ( ASSETCHAINS_ENDSUBSIDY == 0 || nHeight < ASSETCHAINS_ENDSUBSIDY ) + { + if ( ASSETCHAINS_REWARD == 0 ) + return(10000); + else if ( ASSETCHAINS_ENDSUBSIDY != 0 && nHeight >= ASSETCHAINS_ENDSUBSIDY ) + return(0); + else + { + nSubsidy = ASSETCHAINS_REWARD; + if ( ASSETCHAINS_HALVING != 0 ) + { + if ( (numhalvings= (nHeight / ASSETCHAINS_HALVING)) > 0 ) + { + if ( numhalvings >= 64 && ASSETCHAINS_DECAY == 0 ) + return(0); + if ( ASSETCHAINS_DECAY == 0 ) + nSubsidy >>= numhalvings; + else if ( ASSETCHAINS_DECAY == 100000000 && ASSETCHAINS_ENDSUBSIDY != 0 ) + { + numerator = (ASSETCHAINS_ENDSUBSIDY - nHeight); + nSubsidy = (nSubsidy * numerator) / ASSETCHAINS_ENDSUBSIDY; + } + else + { + if ( cached_subsidy > 0 && cached_numhalvings == numhalvings ) + nSubsidy = cached_subsidy; + else + { + for (i=0; i<numhalvings&&nSubsidy!=0; i++) + nSubsidy = (nSubsidy * ASSETCHAINS_DECAY) / 100000000; + cached_subsidy = nSubsidy; + cached_numhalvings = numhalvings; + } + } + } + } + } + return(nSubsidy); + } else return(0); + } +/* // Mining slow start // The subsidy is ramped up linearly, skipping the middle payout of // MAX_SUBSIDY/2 to keep the monetary curve consistent with no slow start. @@ -1520,13 +1709,14 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) } assert(nHeight > consensusParams.SubsidySlowStartShift()); - int halvings = (nHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nSubsidyHalvingInterval; + int halvings = (nHeight - consensusParams.SubsidySlowStartShift()) / consensusParams.nSubsidyHalvingInterval;*/ // Force block reward to zero when right shift is undefined. - if (halvings >= 64) - return 0; + //int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; + //if (halvings >= 64) + // return 0; // Subsidy is cut in half every 840,000 blocks which will occur approximately every 4 years. - nSubsidy >>= halvings; + //nSubsidy >>= halvings; return nSubsidy; } @@ -1535,16 +1725,35 @@ bool IsInitialBlockDownload() const CChainParams& chainParams = Params(); LOCK(cs_main); if (fImporting || fReindex) + { + //fprintf(stderr,"IsInitialBlockDownload: fImporting %d || %d fReindex\n",(int32_t)fImporting,(int32_t)fReindex); return true; + } if (fCheckpointsEnabled && chainActive.Height() < Checkpoints::GetTotalBlocksEstimate(chainParams.Checkpoints())) + { + //fprintf(stderr,"IsInitialBlockDownload: checkpoint -> initialdownload\n"); return true; + } static bool lockIBDState = false; if (lockIBDState) + { + //fprintf(stderr,"lockIBDState true %d < %d\n",chainActive.Height(),pindexBestHeader->nHeight - 10); return false; - bool state = (chainActive.Height() < pindexBestHeader->nHeight - 24 * 6 || - pindexBestHeader->GetBlockTime() < GetTime() - chainParams.MaxTipAge()); + } + bool state; CBlockIndex *ptr = chainActive.Tip(); + if ( ptr == 0 ) + ptr = pindexBestHeader; + else if ( pindexBestHeader != 0 && pindexBestHeader->nHeight > ptr->nHeight ) + ptr = pindexBestHeader; + //if ( ASSETCHAINS_SYMBOL[0] == 0 ) + state = ((chainActive.Height() < ptr->nHeight - 24*60) || + ptr->GetBlockTime() < (GetTime() - chainParams.MaxTipAge())); + //else state = (chainActive.Height() < ptr->nHeight - 24*60); + //fprintf(stderr,"state.%d ht.%d vs %d, t.%u %u\n",state,(int32_t)chainActive.Height(),(uint32_t)ptr->nHeight,(int32_t)ptr->GetBlockTime(),(uint32_t)(GetTime() - chainParams.MaxTipAge())); if (!state) + { lockIBDState = true; + } return state; } @@ -1686,8 +1895,8 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight) { - // mark inputs spent - if (!tx.IsCoinBase()) { + if (!tx.IsCoinBase()) // mark inputs spent + { txundo.vprevout.reserve(tx.vin.size()); BOOST_FOREACH(const CTxIn &txin, tx.vin) { CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash); @@ -1706,16 +1915,12 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund } } } - - // spend nullifiers - BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { + BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { // spend nullifiers BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { inputs.SetNullifier(nf, true); } } - - // add outputs - inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); + inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); // add outputs } void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) @@ -1726,7 +1931,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore), &error)) { + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), consensusBranchId, &error)) { return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); } return true; @@ -1780,6 +1985,20 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins // Check for negative or overflow input values nValueIn += coins->vout[prevout.n].nValue; +#ifdef KOMODO_ENABLE_INTEREST + if ( ASSETCHAINS_SYMBOL[0] == 0 && chainActive.Tip() != 0 && chainActive.Tip()->nHeight >= 60000 ) + { + if ( coins->vout[prevout.n].nValue >= 10*COIN ) + { + int64_t interest; int32_t txheight; uint32_t locktime; + if ( (interest= komodo_accrued_interest(&txheight,&locktime,prevout.hash,prevout.n,0,coins->vout[prevout.n].nValue)) != 0 ) + { + //fprintf(stderr,"checkResult %.8f += val %.8f interest %.8f ht.%d lock.%u tip.%u\n",(double)nValueIn/COIN,(double)coins->vout[prevout.n].nValue/COIN,(double)interest/COIN,txheight,locktime,chainActive.Tip()->nTime); + nValueIn += interest; + } + } + } +#endif if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn)) return state.DoS(100, error("CheckInputs(): txin values out of range"), REJECT_INVALID, "bad-txns-inputvalues-outofrange"); @@ -1792,9 +2011,8 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins REJECT_INVALID, "bad-txns-inputvalues-outofrange"); if (nValueIn < tx.GetValueOut()) - return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s)", - tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut())), - REJECT_INVALID, "bad-txns-in-belowout"); + return state.DoS(100, error("CheckInputs(): %s value in (%s) < value out (%s) diff %.8f", + tx.GetHash().ToString(), FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()),((double)nValueIn - tx.GetValueOut())/COIN),REJECT_INVALID, "bad-txns-in-belowout"); // Tally transaction fees CAmount nTxFee = nValueIn - tx.GetValueOut(); @@ -1809,7 +2027,17 @@ bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoins } }// namespace Consensus -bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks) +bool ContextualCheckInputs( + const CTransaction& tx, + CValidationState &state, + const CCoinsViewCache &inputs, + bool fScriptChecks, + unsigned int flags, + bool cacheStore, + PrecomputedTransactionData& txdata, + const Consensus::Params& consensusParams, + uint32_t consensusBranchId, + std::vector<CScriptCheck> *pvChecks) { if (!tx.IsCoinBase()) { @@ -1834,7 +2062,7 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons assert(coins); // Verify signature - CScriptCheck check(*coins, tx, i, flags, cacheStore); + CScriptCheck check(*coins, tx, i, flags, cacheStore, consensusBranchId, &txdata); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1847,7 +2075,7 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons // avoid splitting the network between upgraded and // non-upgraded nodes. CScriptCheck check2(*coins, tx, i, - flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore); + flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheStore, consensusBranchId, &txdata); if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } @@ -1867,6 +2095,45 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons return true; } + +/*bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, const Consensus::Params& consensusParams, std::vector<CScriptCheck> *pvChecks) +{ + if (!NonContextualCheckInputs(tx, state, inputs, fScriptChecks, flags, cacheStore, consensusParams, pvChecks)) { + fprintf(stderr,"ContextualCheckInputs failure.0\n"); + return false; + } + + if (!tx.IsCoinBase()) + { + // While checking, GetBestBlock() refers to the parent block. + // This is also true for mempool checks. + CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + int nSpendHeight = pindexPrev->nHeight + 1; + for (unsigned int i = 0; i < tx.vin.size(); i++) + { + const COutPoint &prevout = tx.vin[i].prevout; + const CCoins *coins = inputs.AccessCoins(prevout.hash); + // Assertion is okay because NonContextualCheckInputs ensures the inputs + // are available. + assert(coins); + + // If prev is coinbase, check that it's matured + if (coins->IsCoinBase()) { + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + COINBASE_MATURITY = _COINBASE_MATURITY; + if (nSpendHeight - coins->nHeight < COINBASE_MATURITY) { + fprintf(stderr,"ContextualCheckInputs failure.1 i.%d of %d\n",i,(int32_t)tx.vin.size()); + + return state.Invalid( + error("CheckInputs(): tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight),REJECT_INVALID, "bad-txns-premature-spend-of-coinbase"); + } + } + } + } + + return true; +}*/ + namespace { bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint256& hashBlock, const CMessageHeader::MessageStartChars& messageStart) @@ -1984,7 +2251,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex *pfClean = false; bool fClean = true; - + komodo_disconnect(pindex,block); CBlockUndo blockUndo; CDiskBlockPos pos = pindex->GetUndoPos(); if (pos.IsNull()) @@ -2115,7 +2382,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const while (i->GetBlockTime() >= startTime) { ++nBlocks; i = i->pprev; - if (i == NULL) return; // Ran out of chain, we must not be fully sync'ed + if (i == NULL) return; // Ran out of chain, we must not be fully synced } // How likely is it to find that many by chance? @@ -2157,8 +2424,8 @@ static int64_t nTimeTotal = 0; bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) { const CChainParams& chainparams = Params(); + //fprintf(stderr,"connectblock ht.%d\n",(int32_t)pindex->nHeight); AssertLockHeld(cs_main); - bool fExpensiveChecks = true; if (fCheckpointsEnabled) { CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); @@ -2167,12 +2434,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin fExpensiveChecks = false; } } - 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)) + if (!CheckBlock(pindex->nHeight,pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block @@ -2193,6 +2459,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return true; } + bool fScriptChecks = (!fCheckpointsEnabled || pindex->nHeight >= Checkpoints::GetTotalBlocksEstimate(chainparams.Checkpoints())); + //if ( KOMODO_TESTNET_EXPIRATION != 0 && pindex->nHeight > KOMODO_TESTNET_EXPIRATION ) // "testnet" + // return(false); // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. BOOST_FOREACH(const CTransaction& tx, block.vtx) { @@ -2213,6 +2482,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTimeStart = GetTimeMicros(); CAmount nFees = 0; int nInputs = 0; + int64_t interest,sum = 0; unsigned int nSigOps = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector<std::pair<uint256, CDiskTxPos> > vPos; @@ -2237,16 +2507,20 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin assert(tree.root() == old_tree_root); } + // Grab the consensus branch ID for the block's height + auto consensusBranchId = CurrentEpochBranchId(pindex->nHeight, Params().GetConsensus()); + + std::vector<PrecomputedTransactionData> txdata; + txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated for (unsigned int i = 0; i < block.vtx.size(); i++) { const CTransaction &tx = block.vtx[i]; - nInputs += tx.vin.size(); nSigOps += GetLegacySigOpCount(tx); if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); - +//fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime); if (!tx.IsCoinBase()) { if (!view.HaveInputs(tx)) @@ -2265,15 +2539,22 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); + } - nFees += view.GetValueIn(tx)-tx.GetValueOut(); + txdata.emplace_back(tx); + + if (!tx.IsCoinBase()) + { + nFees += view.GetValueIn(chainActive.Tip()->nHeight,&interest,tx,chainActive.Tip()->nTime) - tx.GetValueOut(); + sum += interest; std::vector<CScriptCheck> vChecks; - if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, chainparams.GetConsensus(), nScriptCheckThreads ? &vChecks : NULL)) + if (!ContextualCheckInputs(tx, state, view, fExpensiveChecks, flags, false, txdata[i], chainparams.GetConsensus(), consensusBranchId, nScriptCheckThreads ? &vChecks : NULL)) return false; control.Add(vChecks); } - + //if ( ASSETCHAINS_SYMBOL[0] == 0 ) + // komodo_earned_interest(pindex->nHeight,sum); CTxUndo undoDummy; if (i > 0) { blockundo.vtxundo.push_back(CTxUndo()); @@ -2302,7 +2583,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); - if (block.vtx[0].GetValueOut() > blockReward) + if (block.vtx[0].vout[0].nValue > blockReward) + //if (block.vtx[0].GetValueOut() > blockReward) return state.DoS(100, error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", block.vtx[0].GetValueOut(), blockReward), @@ -2363,7 +2645,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTime4 = GetTimeMicros(); nTimeCallbacks += nTime4 - nTime3; LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001); - + + //FlushStateToDisk(); + komodo_connectblock(pindex,*(CBlock *)&block); return true; } @@ -2580,6 +2864,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { SyncWithWallets(tx, NULL); } // Update cached incremental witnesses + //fprintf(stderr,"chaintip false\n"); GetMainSignals().ChainTip(pindexDelete, &block, newTree, false); return true; } @@ -2596,6 +2881,7 @@ static int64_t nTimePostConnect = 0; * You probably want to call mempool.removeWithoutBranchId after this, with cs_main held. */ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *pblock) { + assert(pindexNew->pprev == chainActive.Tip()); // Read block from disk. int64_t nTime1 = GetTimeMicros(); @@ -2636,6 +2922,10 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * // Remove conflicting transactions from the mempool. list<CTransaction> txConflicted; mempool.removeForBlock(pblock->vtx, pindexNew->nHeight, txConflicted, !IsInitialBlockDownload()); + + // Remove transactions that expire at new block height from mempool + mempool.removeExpired(pindexNew->nHeight); + // Update chainActive & related variables. UpdateTip(pindexNew); // Tell wallet about transactions that went from mempool @@ -2648,6 +2938,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * SyncWithWallets(tx, pblock); } // Update cached incremental witnesses + //fprintf(stderr,"chaintip true\n"); GetMainSignals().ChainTip(pindexNew, pblock, oldTree, true); EnforceNodeDeprecation(pindexNew->nHeight); @@ -2738,6 +3029,31 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); + // - On ChainDB initialization, pindexOldTip will be null, so there are no removable blocks. + // - If pindexMostWork is in a chain that doesn't have the same genesis block as our chain, + // then pindexFork will be null, and we would need to remove the entire chain including + // our genesis block. In practice this (probably) won't happen because of checks elsewhere. + auto reorgLength = pindexOldTip ? pindexOldTip->nHeight - (pindexFork ? pindexFork->nHeight : -1) : 0; + static_assert(MAX_REORG_LENGTH > 0, "We must be able to reorg some distance"); + if (reorgLength > MAX_REORG_LENGTH) { + auto msg = strprintf(_( + "A block chain reorganization has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), reorgLength, MAX_REORG_LENGTH) + "\n\n" + + _("Reorganization details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d, work %s"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight, pindexOldTip->nChainWork.GetHex()) + "\n" + + "- " + strprintf(_("New tip: %s, height %d, work %s"), + pindexMostWork->phashBlock->GetHex(), pindexMostWork->nHeight, pindexMostWork->nChainWork.GetHex()) + "\n" + + "- " + strprintf(_("Fork point: %s, height %d"), + pindexFork->phashBlock->GetHex(), pindexFork->nHeight) + "\n\n" + + _("Please help, human!"); + LogPrintf("*** %s\n", msg); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; + } + // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; while (chainActive.Tip() && chainActive.Tip() != pindexFork) { @@ -2745,7 +3061,22 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo return false; fBlocksDisconnected = true; } - + if ( KOMODO_REWIND != 0 ) + { + fprintf(stderr,"rewind start ht.%d\n",chainActive.Tip()->nHeight); + while ( KOMODO_REWIND > 0 && chainActive.Tip()->nHeight > KOMODO_REWIND ) + { + if ( !DisconnectTip(state) ) + { + InvalidateBlock(state,chainActive.Tip()); + break; + } + } + fprintf(stderr,"reached rewind.%d, best to do: ./komodo-cli stop\n",KOMODO_REWIND); + sleep(60); + KOMODO_REWIND = 0; + return(true); + } // Build list of new blocks to connect. std::vector<CBlockIndex*> vpindexToConnect; bool fContinue = true; @@ -2828,7 +3159,6 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) return false; - pindexNewTip = chainActive.Tip(); fInitialDownload = IsInitialBlockDownload(); } @@ -2852,7 +3182,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { // Notify external listeners about the new tip. GetMainSignals().UpdatedBlockTip(pindexNewTip); uiInterface.NotifyBlockTip(hashNewTip); - } + } //else fprintf(stderr,"initial download skips propagation\n"); } while(pindexMostWork != chainActive.Tip()); CheckBlockIndex(); @@ -2886,11 +3216,12 @@ bool InvalidateBlock(CValidationState& state, CBlockIndex *pindex) { return false; } } + //LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); // The resulting new best tip may not be in setBlockIndexCandidates anymore, so // add it again. BlockMap::iterator it = mapBlockIndex.begin(); - while (it != mapBlockIndex.end()) { + while (it != mapBlockIndex.end() && it->second != 0 ) { if (it->second->IsValid(BLOCK_VALID_TRANSACTIONS) && it->second->nChainTx && !setBlockIndexCandidates.value_comp()(it->second, chainActive.Tip())) { setBlockIndexCandidates.insert(it->second); } @@ -3123,32 +3454,44 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne return true; } -bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW) +bool CheckBlockHeader(int32_t height,CBlockIndex *pindex, const CBlockHeader& blockhdr, CValidationState& state, bool fCheckPOW) { + uint8_t pubkey33[33]; + // Check timestamp + if ( 0 ) + { + uint256 hash; int32_t i; + hash = blockhdr.GetHash(); + for (i=31; i>=0; i--) + fprintf(stderr,"%02x",((uint8_t *)&hash)[i]); + fprintf(stderr," <- CheckBlockHeader\n"); + if ( chainActive.Tip() != 0 ) + { + hash = chainActive.Tip()->GetBlockHash(); + for (i=31; i>=0; i--) + fprintf(stderr,"%02x",((uint8_t *)&hash)[i]); + fprintf(stderr," <- chainTip\n"); + } + } + if (blockhdr.GetBlockTime() > GetAdjustedTime() + 60) + return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"),REJECT_INVALID, "time-too-new"); // Check block version - if (block.nVersion < MIN_BLOCK_VERSION) - return state.DoS(100, error("CheckBlockHeader(): block version too low"), - REJECT_INVALID, "version-too-low"); + //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"), - REJECT_INVALID, "invalid-solution"); - + if ( fCheckPOW && !CheckEquihashSolution(&blockhdr, Params()) ) + return state.DoS(100, error("CheckBlockHeader(): Equihash solution invalid"),REJECT_INVALID, "invalid-solution"); + // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) - return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), - REJECT_INVALID, "high-hash"); - - // Check timestamp - if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) - return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"), - REJECT_INVALID, "time-too-new"); - + komodo_index2pubkey33(pubkey33,pindex,height); + if ( fCheckPOW && !CheckProofOfWork(height,pubkey33,blockhdr.GetHash(), blockhdr.nBits, Params().GetConsensus()) ) + return state.DoS(50, error("CheckBlockHeader(): proof of work failed"),REJECT_INVALID, "high-hash"); return true; } -bool CheckBlock(const CBlock& block, CValidationState& state, +int32_t komodo_check_deposit(int32_t height,const CBlock& block); +bool CheckBlock(int32_t height,CBlockIndex *pindex,const CBlock& block, CValidationState& state, libzcash::ProofVerifier& verifier, bool fCheckPOW, bool fCheckMerkleRoot) { @@ -3156,7 +3499,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, // Check that the header is valid (particularly PoW). This is mostly // redundant with the call in AcceptBlockHeader. - if (!CheckBlockHeader(block, state, fCheckPOW)) + if (!CheckBlockHeader(height,pindex,block,state,fCheckPOW)) return false; // Check the merkle root. @@ -3195,9 +3538,12 @@ bool CheckBlock(const CBlock& block, CValidationState& state, // Check transactions BOOST_FOREACH(const CTransaction& tx, block.vtx) + { + if ( komodo_validate_interest(tx,komodo_block2height((CBlock *)&block),block.nTime,1) < 0 ) + return error("CheckBlock: komodo_validate_interest failed"); if (!CheckTransaction(tx, state, verifier)) return error("CheckBlock(): CheckTransaction failed"); - + } unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) { @@ -3206,7 +3552,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, if (nSigOps > MAX_BLOCK_SIGOPS) return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), REJECT_INVALID, "bad-blk-sigops", true); - + if ( komodo_check_deposit(ASSETCHAINS_SYMBOL[0] == 0 ? height : pindex != 0 ? (int32_t)pindex->nHeight : chainActive.Tip()->nHeight+1,block) < 0 ) + { + static uint32_t counter; + if ( counter++ < 100 ) + fprintf(stderr,"check deposit rejection\n"); + return(false); + } return true; } @@ -3223,9 +3575,12 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta int nHeight = pindexPrev->nHeight+1; // Check proof of work - if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) + if ( (nHeight < 235300 || nHeight > 236000) && block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) + { + cout << block.nBits << " block.nBits vs. calc " << GetNextWorkRequired(pindexPrev, &block, consensusParams) << endl; return state.DoS(100, error("%s: incorrect proof of work", __func__), REJECT_INVALID, "bad-diffbits"); + } // Check timestamp against prev if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) @@ -3234,12 +3589,25 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta if (fCheckpointsEnabled) { + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints::CheckBlock(chainParams.Checkpoints(), nHeight, hash)) + return state.DoS(100, error("%s: rejected by checkpoint lock-in at %d", __func__, nHeight),REJECT_CHECKPOINT, "checkpoint mismatch"); + // Don't accept any forks from the main chain prior to last checkpoint CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); + int32_t notarized_height; + if (pcheckpoint && (nHeight < pcheckpoint->nHeight || nHeight == 1 && chainActive.Tip() != 0 && chainActive.Tip()->nHeight > 1) ) + return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d) vs %d", __func__, nHeight,pcheckpoint->nHeight)); + else if ( komodo_checkpoint(¬arized_height,nHeight,hash) < 0 ) + { + CBlockIndex *heightblock = chainActive[nHeight]; + if ( heightblock != 0 && heightblock->GetBlockHash() == hash ) + { + //fprintf(stderr,"got a pre notarization block that matches height.%d\n",(int32_t)nHeight); + return true; + } else return state.DoS(100, error("%s: forked chain %d older than last notarized (height %d) vs %d", __func__,nHeight, notarized_height)); + } } - // Reject block.nVersion < 4 blocks if (block.nVersion < 4) return state.Invalid(error("%s : rejected nVersion<4 block", __func__), @@ -3283,28 +3651,6 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn } } - // Coinbase transaction must include an output sending 20% of - // the block reward to a founders reward script, until the last founders - // reward block is reached, with exception of the genesis block. - // The last founders reward block is defined as the block just before the - // first subsidy halving block, which occurs at halving_interval + slow_start_shift - if ((nHeight > 0) && (nHeight <= consensusParams.GetLastFoundersRewardBlockHeight())) { - bool found = false; - - BOOST_FOREACH(const CTxOut& output, block.vtx[0].vout) { - if (output.scriptPubKey == Params().GetFoundersRewardScriptAtHeight(nHeight)) { - if (output.nValue == (GetBlockSubsidy(nHeight, consensusParams) / 5)) { - found = true; - break; - } - } - } - - if (!found) { - return state.DoS(100, error("%s: founders reward missing", __func__), REJECT_INVALID, "cb-no-founders-reward"); - } - } - return true; } @@ -3321,12 +3667,46 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc pindex = miSelf->second; if (ppindex) *ppindex = pindex; - if (pindex->nStatus & BLOCK_FAILED_MASK) + if (pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK) return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); + if ( pindex != 0 && IsInitialBlockDownload() == 0 ) // jl777 debug test + { + if (!CheckBlockHeader(pindex->nHeight,pindex, block, state)) + { + pindex->nStatus |= BLOCK_FAILED_MASK; + fprintf(stderr,"known block failing CheckBlockHeader %d\n",(int32_t)pindex->nHeight); + return false; + } + CBlockIndex* pindexPrev = NULL; + if (hash != chainparams.GetConsensus().hashGenesisBlock) + { + BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); + if (mi == mapBlockIndex.end()) + { + pindex->nStatus |= BLOCK_FAILED_MASK; + fprintf(stderr,"known block.%d failing to find prevblock\n",(int32_t)pindex->nHeight); + return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); + } + pindexPrev = (*mi).second; + if (pindexPrev == 0 || (pindexPrev->nStatus & BLOCK_FAILED_MASK) ) + { + pindex->nStatus |= BLOCK_FAILED_MASK; + fprintf(stderr,"known block.%d found invalid prevblock\n",(int32_t)pindex->nHeight); + return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); + } + } + if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + { + pindex->nStatus |= BLOCK_FAILED_MASK; + fprintf(stderr,"known block.%d failing ContextualCheckBlockHeader\n",(int32_t)pindex->nHeight); + return false; + } + } + return true; } - if (!CheckBlockHeader(block, state)) + if (!CheckBlockHeader(*ppindex!=0?(*ppindex)->nHeight:0,*ppindex, block, state)) return false; // Get prev block index @@ -3334,21 +3714,19 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc if (hash != chainparams.GetConsensus().hashGenesisBlock) { BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) + { return state.DoS(10, error("%s: prev block not found", __func__), 0, "bad-prevblk"); + } pindexPrev = (*mi).second; - if (pindexPrev->nStatus & BLOCK_FAILED_MASK) + if (pindexPrev == 0 || (pindexPrev->nStatus & BLOCK_FAILED_MASK) ) return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } - if (!ContextualCheckBlockHeader(block, state, pindexPrev)) return false; - if (pindex == NULL) pindex = AddToBlockIndex(block); - if (ppindex) *ppindex = pindex; - return true; } @@ -3358,10 +3736,13 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, AssertLockHeld(cs_main); CBlockIndex *&pindex = *ppindex; - if (!AcceptBlockHeader(block, state, &pindex)) return false; - + if ( pindex == 0 ) + { + fprintf(stderr,"AcceptBlock error null pindex\n"); + return false; + } // Try to process all requested blocks that we don't have, but only // process an unrequested block if it's new and has enough work to // advance our tip, and isn't too many blocks ahead. @@ -3385,7 +3766,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, // 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 ((!CheckBlock(pindex->nHeight,pindex,block, state, verifier)) || !ContextualCheckBlock(block, state, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); @@ -3430,18 +3811,25 @@ static bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned return (nFound >= nRequired); } +void komodo_currentheight_set(int32_t height); -bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) +bool ProcessNewBlock(int32_t height,CValidationState &state, CNode* pfrom, CBlock* pblock, bool fForceProcessing, CDiskBlockPos *dbp) { // Preliminary checks + bool checked; auto verifier = libzcash::ProofVerifier::Disabled(); - bool checked = CheckBlock(*pblock, state, verifier); - + if ( chainActive.Tip() != 0 ) + komodo_currentheight_set(chainActive.Tip()->nHeight); + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier); + else checked = CheckBlock(height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier); { LOCK(cs_main); bool fRequested = MarkBlockAsReceived(pblock->GetHash()); fRequested |= fForceProcessing; if (!checked) { + if ( pfrom != 0 ) + Misbehaving(pfrom->GetId(), 1); return error("%s: CheckBlock FAILED", __func__); } @@ -3476,13 +3864,25 @@ bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex // NOTE: CheckBlockHeader is called by CheckBlock if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + { + fprintf(stderr,"TestBlockValidity failure A\n"); return false; - if (!CheckBlock(block, state, verifier, fCheckPOW, fCheckMerkleRoot)) + } + if (!CheckBlock(indexDummy.nHeight,0,block, state, verifier, fCheckPOW, fCheckMerkleRoot)) + { + //fprintf(stderr,"TestBlockValidity failure B\n"); return false; + } if (!ContextualCheckBlock(block, state, pindexPrev)) + { + fprintf(stderr,"TestBlockValidity failure C\n"); return false; + } if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) + { + fprintf(stderr,"TestBlockValidity failure D\n"); return false; + } assert(state.IsValid()); return true; @@ -3713,7 +4113,7 @@ bool static LoadBlockIndexDB() pindex->nCachedBranchId = pindex->pprev->nCachedBranchId; } } else { - pindex->nCachedBranchId = NetworkUpgradeInfo[Consensus::BASE_SPROUT].nBranchId; + pindex->nCachedBranchId = SPROUT_BRANCH_ID; } if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS) && (pindex->nChainTx || pindex->pprev == NULL)) setBlockIndexCandidates.insert(pindex); @@ -3849,7 +4249,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth 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, verifier)) + if (nCheckLevel >= 1 && !CheckBlock(pindex->nHeight,pindex,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) { @@ -3929,6 +4329,26 @@ bool RewindBlockIndex(const CChainParams& params) } // nHeight is now the height of the first insufficiently-validated block, or tipheight + 1 + auto rewindLength = chainActive.Height() - nHeight; + if (rewindLength > 0 && rewindLength > MAX_REORG_LENGTH) { + auto pindexOldTip = chainActive.Tip(); + auto pindexRewind = chainActive[nHeight - 1]; + auto msg = strprintf(_( + "A block chain rewind has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), rewindLength, MAX_REORG_LENGTH) + "\n\n" + + _("Rewind details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->nHeight) + "\n" + + "- " + strprintf(_("Rewinding to: %s, height %d"), + pindexRewind->phashBlock->GetHex(), pindexRewind->nHeight) + "\n\n" + + _("Please help, human!"); + LogPrintf("*** %s\n", msg); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; + } + CValidationState state; CBlockIndex* pindex = chainActive.Tip(); while (chainActive.Height() >= nHeight) { @@ -4040,9 +4460,16 @@ void UnloadBlockIndex() bool LoadBlockIndex() { + extern int32_t KOMODO_LOADINGBLOCKS; // Load block index from databases + KOMODO_LOADINGBLOCKS = 1; if (!fReindex && !LoadBlockIndexDB()) + { + KOMODO_LOADINGBLOCKS = 0; return false; + } + KOMODO_LOADINGBLOCKS = 0; + fprintf(stderr,"finished loading blocks %s\n",ASSETCHAINS_SYMBOL); return true; } @@ -4059,7 +4486,7 @@ bool InitBlockIndex() { return true; // Use the provided setting for -txindex in the new database - fTxIndex = GetBoolArg("-txindex", false); + fTxIndex = GetBoolArg("-txindex", true); pblocktree->WriteFlag("txindex", fTxIndex); LogPrintf("Initializing databases...\n"); @@ -4151,7 +4578,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) // process in case the block isn't known yet if (mapBlockIndex.count(hash) == 0 || (mapBlockIndex[hash]->nStatus & BLOCK_HAVE_DATA) == 0) { CValidationState state; - if (ProcessNewBlock(state, NULL, &block, true, dbp)) + if (ProcessNewBlock(0,state, NULL, &block, true, dbp)) nLoaded++; if (state.IsError()) break; @@ -4168,12 +4595,12 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) std::pair<std::multimap<uint256, CDiskBlockPos>::iterator, std::multimap<uint256, CDiskBlockPos>::iterator> range = mapBlocksUnknownParent.equal_range(head); while (range.first != range.second) { std::multimap<uint256, CDiskBlockPos>::iterator it = range.first; - if (ReadBlockFromDisk(block, it->second)) + if (ReadBlockFromDisk(mapBlockIndex[hash]!=0?mapBlockIndex[hash]->nHeight:0,block, it->second)) { LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), head.ToString()); CValidationState dummy; - if (ProcessNewBlock(dummy, NULL, &block, true, &it->second)) + if (ProcessNewBlock(0,dummy, NULL, &block, true, &it->second)) { nLoaded++; queue.push_back(block.GetHash()); @@ -4215,7 +4642,7 @@ void static CheckBlockIndex() // Build forward-pointing map of the entire block tree. std::multimap<CBlockIndex*,CBlockIndex*> forward; for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); it++) { - forward.insert(std::make_pair(it->second->pprev, it->second)); + forward.insert(std::make_pair(it->second->pprev, it->second)); } assert(forward.size() == mapBlockIndex.size()); @@ -4528,31 +4955,42 @@ void static ProcessGetData(CNode* pfrom) // Send block from disk CBlock block; if (!ReadBlockFromDisk(block, (*mi).second)) + { assert(!"cannot load block from disk"); - if (inv.type == MSG_BLOCK) - pfrom->PushMessage("block", block); - else // MSG_FILTERED_BLOCK) + } + else { - LOCK(pfrom->cs_filter); - if (pfrom->pfilter) + if (inv.type == MSG_BLOCK) { - CMerkleBlock merkleBlock(block, *pfrom->pfilter); - pfrom->PushMessage("merkleblock", merkleBlock); - // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see - // This avoids hurting performance by pointlessly requiring a round-trip - // Note that there is currently no way for a node to request any single transactions we didn't send here - - // they must either disconnect and retry or request the full block. - // Thus, the protocol spec specified allows for us to provide duplicate txn here, - // however we MUST always provide at least what the remote peer needs - typedef std::pair<unsigned int, uint256> PairType; - BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) + //uint256 hash; int32_t z; + //hash = block.GetHash(); + //for (z=31; z>=0; z--) + // fprintf(stderr,"%02x",((uint8_t *)&hash)[z]); + //fprintf(stderr," send block %d\n",komodo_block2height(&block)); + pfrom->PushMessage("block", block); + } + else // MSG_FILTERED_BLOCK) + { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) + { + CMerkleBlock merkleBlock(block, *pfrom->pfilter); + pfrom->PushMessage("merkleblock", merkleBlock); + // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see + // This avoids hurting performance by pointlessly requiring a round-trip + // Note that there is currently no way for a node to request any single transactions we didn't send here - + // they must either disconnect and retry or request the full block. + // Thus, the protocol spec specified allows for us to provide duplicate txn here, + // however we MUST always provide at least what the remote peer needs + typedef std::pair<unsigned int, uint256> PairType; + BOOST_FOREACH(PairType& pair, merkleBlock.vMatchedTxn) if (!pfrom->setInventoryKnown.count(CInv(MSG_TX, pair.second))) pfrom->PushMessage("tx", block.vtx[pair.first]); - } - // else + } + // else // no response + } } - // Trigger the peer node to send a getblocks request for the next batch of inventory if (inv.hash == pfrom->hashContinue) { @@ -4619,6 +5057,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, { const CChainParams& chainparams = Params(); LogPrint("net", "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->id); + //fprintf(stderr, "recv: %s peer=%d\n", SanitizeString(strCommand).c_str(), (int32_t)pfrom->GetId()); if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) { LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n"); @@ -4653,6 +5092,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return false; } + // When Overwinter is active, reject incoming connections from non-Overwinter nodes + const Consensus::Params& params = Params().GetConsensus(); + if (NetworkUpgradeActive(GetHeight(), params, Consensus::UPGRADE_OVERWINTER) + && pfrom->nVersion < params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion) + { + LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", + params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); + pfrom->fDisconnect = true; + return false; + } + if (pfrom->nVersion == 10300) pfrom->nVersion = 300; if (!vRecv.empty()) @@ -4771,6 +5223,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } + // Disconnect existing peer connection when: + // 1. The version message has been received + // 2. Overwinter is active + // 3. Peer version is pre-Overwinter + else if (NetworkUpgradeActive(GetHeight(), chainparams.GetConsensus(), Consensus::UPGRADE_OVERWINTER) + && (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)) + { + LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); + pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", + chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)); + pfrom->fDisconnect = true; + return false; + } + + else if (strCommand == "addr") { vector<CAddress> vAddr; @@ -4992,13 +5460,23 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, vector<CBlock> vHeaders; int nLimit = MAX_HEADERS_RESULTS; LogPrint("net", "getheaders %d to %s from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString(), pfrom->id); - for (; pindex; pindex = chainActive.Next(pindex)) + if ( pfrom->lasthdrsreq >= chainActive.Height()-MAX_HEADERS_RESULTS || pfrom->lasthdrsreq != (int32_t)(pindex ? pindex->nHeight : -1) ) { - vHeaders.push_back(pindex->GetBlockHeader()); - if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) - break; + pfrom->lasthdrsreq = (int32_t)(pindex ? pindex->nHeight : -1); + for (; pindex; pindex = chainActive.Next(pindex)) + { + vHeaders.push_back(pindex->GetBlockHeader()); + if (--nLimit <= 0 || pindex->GetBlockHash() == hashStop) + break; + } + pfrom->PushMessage("headers", vHeaders); + } + else if ( NOTARY_PUBKEY33[0] != 0 ) + { + static uint32_t counter; + if ( counter++ < 3 ) + fprintf(stderr,"you can ignore redundant getheaders from peer.%d %d prev.%d\n",(int32_t)pfrom->id,(int32_t)(pindex ? pindex->nHeight : -1),pfrom->lasthdrsreq); } - pfrom->PushMessage("headers", vHeaders); } @@ -5166,7 +5644,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int nDoS; if (state.IsInvalid(nDoS)) { if (nDoS > 0) - Misbehaving(pfrom->GetId(), nDoS); + Misbehaving(pfrom->GetId(), nDoS/nDoS); return error("invalid header received"); } } @@ -5179,8 +5657,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Headers message had its maximum size; the peer may have more headers. // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. - LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); - pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); + if ( pfrom->sendhdrsreq >= chainActive.Height()-MAX_HEADERS_RESULTS || pindexLast->nHeight != pfrom->sendhdrsreq ) + { + pfrom->sendhdrsreq = (int32_t)pindexLast->nHeight; + LogPrint("net", "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->id, pfrom->nStartingHeight); + pfrom->PushMessage("getheaders", chainActive.GetLocator(pindexLast), uint256()); + } } CheckBlockIndex(); @@ -5202,7 +5684,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Such an unrequested block may still be processed, subject to the // conditions in AcceptBlock(). bool forceProcessing = pfrom->fWhitelisted && !IsInitialBlockDownload(); - ProcessNewBlock(state, pfrom, &block, forceProcessing, NULL); + ProcessNewBlock(0,state, pfrom, &block, forceProcessing, NULL); int nDoS; if (state.IsInvalid(nDoS)) { pfrom->PushMessage("reject", strCommand, state.GetRejectCode(), @@ -5445,7 +5927,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } } } - 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. @@ -5556,7 +6037,7 @@ bool ProcessMessages(CNode* pfrom) } else { - PrintExceptionContinue(&e, "ProcessMessages()"); + //PrintExceptionContinue(&e, "ProcessMessages()"); } } catch (const boost::thread_interrupted&) { @@ -5864,6 +6345,11 @@ public: } } instance_of_cmaincleanup; +extern "C" const char* getDataDir() +{ + return GetDataDir().string().c_str(); +} + // Set default values of new CMutableTransaction based on consensus rules at given height. CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight)