|
| 1 | +// Copyright (c) 2017-2017 The Bitcoin Core developers |
| 2 | +// Distributed under the MIT software license, see the accompanying |
| 3 | +// file COPYING or http://www.opensource.org/licenses/mit-license.php. |
| 4 | + |
| 5 | +#include "tx_verify.h" |
| 6 | + |
| 7 | +#include "consensus/consensus.h" |
| 8 | +#include "main.h" |
| 9 | +#include "script/interpreter.h" |
| 10 | + |
| 11 | +bool IsFinalTx(const CTransaction& tx, int nBlockHeight, int64_t nBlockTime) |
| 12 | +{ |
| 13 | + AssertLockHeld(cs_main); |
| 14 | + // Time based nLockTime implemented in 0.1.6 |
| 15 | + if (tx.nLockTime == 0) |
| 16 | + return true; |
| 17 | + if (nBlockHeight == 0) |
| 18 | + nBlockHeight = chainActive.Height(); |
| 19 | + if (nBlockTime == 0) |
| 20 | + nBlockTime = GetAdjustedTime(); |
| 21 | + if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime)) |
| 22 | + return true; |
| 23 | + for (const CTxIn& txin : tx.vin) |
| 24 | + if (!txin.IsFinal()) |
| 25 | + return false; |
| 26 | + return true; |
| 27 | +} |
| 28 | + |
| 29 | +unsigned int GetLegacySigOpCount(const CTransaction& tx) |
| 30 | +{ |
| 31 | + unsigned int nSigOps = 0; |
| 32 | + for (const CTxIn& txin : tx.vin) { |
| 33 | + nSigOps += txin.scriptSig.GetSigOpCount(false); |
| 34 | + } |
| 35 | + for (const CTxOut& txout : tx.vout) { |
| 36 | + nSigOps += txout.scriptPubKey.GetSigOpCount(false); |
| 37 | + } |
| 38 | + return nSigOps; |
| 39 | +} |
| 40 | + |
| 41 | +unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs) |
| 42 | +{ |
| 43 | + if (tx.IsCoinBase() || tx.HasZerocoinSpendInputs()) |
| 44 | + // a tx containing a zc spend can have only zc inputs |
| 45 | + return 0; |
| 46 | + |
| 47 | + unsigned int nSigOps = 0; |
| 48 | + for (unsigned int i = 0; i < tx.vin.size(); i++) { |
| 49 | + const CTxOut& prevout = inputs.GetOutputFor(tx.vin[i]); |
| 50 | + if (prevout.scriptPubKey.IsPayToScriptHash()) |
| 51 | + nSigOps += prevout.scriptPubKey.GetSigOpCount(tx.vin[i].scriptSig); |
| 52 | + } |
| 53 | + return nSigOps; |
| 54 | +} |
| 55 | + |
| 56 | +bool CheckTransaction(const CTransaction& tx, bool fZerocoinActive, bool fRejectBadUTXO, CValidationState& state, bool fFakeSerialAttack, bool fColdStakingActive) |
| 57 | +{ |
| 58 | + // Basic checks that don't depend on any context |
| 59 | + if (tx.vin.empty()) |
| 60 | + return state.DoS(10, error("CheckTransaction() : vin empty"), |
| 61 | + REJECT_INVALID, "bad-txns-vin-empty"); |
| 62 | + if (tx.vout.empty()) |
| 63 | + return state.DoS(10, error("CheckTransaction() : vout empty"), |
| 64 | + REJECT_INVALID, "bad-txns-vout-empty"); |
| 65 | + |
| 66 | + // Size limits |
| 67 | + unsigned int nMaxSize = MAX_ZEROCOIN_TX_SIZE; |
| 68 | + |
| 69 | + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) > nMaxSize) |
| 70 | + return state.DoS(100, error("CheckTransaction() : size limits failed"), |
| 71 | + REJECT_INVALID, "bad-txns-oversize"); |
| 72 | + |
| 73 | + const CAmount minColdStakingAmount = Params().GetMinColdStakingAmount(); |
| 74 | + |
| 75 | + // Check for negative or overflow output values |
| 76 | + CAmount nValueOut = 0; |
| 77 | + for (const CTxOut& txout : tx.vout) { |
| 78 | + if (txout.IsEmpty() && !tx.IsCoinBase() && !tx.IsCoinStake()) |
| 79 | + return state.DoS(100, error("CheckTransaction(): txout empty for user transaction")); |
| 80 | + if (txout.nValue < 0) |
| 81 | + return state.DoS(100, error("CheckTransaction() : txout.nValue negative"), |
| 82 | + REJECT_INVALID, "bad-txns-vout-negative"); |
| 83 | + if (txout.nValue > MAX_MONEY_OUT) |
| 84 | + return state.DoS(100, error("CheckTransaction() : txout.nValue too high"), |
| 85 | + REJECT_INVALID, "bad-txns-vout-toolarge"); |
| 86 | + nValueOut += txout.nValue; |
| 87 | + if (!MoneyRange(nValueOut)) |
| 88 | + return state.DoS(100, error("CheckTransaction() : txout total out of range"), |
| 89 | + REJECT_INVALID, "bad-txns-txouttotal-toolarge"); |
| 90 | + if (fZerocoinActive && txout.IsZerocoinMint()) { |
| 91 | + if(!CheckZerocoinMint(tx.GetHash(), txout, state, true)) |
| 92 | + return state.DoS(100, error("CheckTransaction() : invalid zerocoin mint")); |
| 93 | + } |
| 94 | + // check cold staking enforcement (for delegations) and value out |
| 95 | + if (txout.scriptPubKey.IsPayToColdStaking()) { |
| 96 | + if (!fColdStakingActive) |
| 97 | + return state.DoS(10, error("%s: cold staking not active", __func__), REJECT_INVALID, "bad-txns-cold-stake"); |
| 98 | + if (txout.nValue < minColdStakingAmount) |
| 99 | + return state.DoS(100, error("%s: dust amount (%d) not allowed for cold staking. Min amount: %d", |
| 100 | + __func__, txout.nValue, minColdStakingAmount), REJECT_INVALID, "bad-txns-cold-stake"); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + std::set<COutPoint> vInOutPoints; |
| 105 | + std::set<CBigNum> vZerocoinSpendSerials; |
| 106 | + int nZCSpendCount = 0; |
| 107 | + |
| 108 | + for (const CTxIn& txin : tx.vin) { |
| 109 | + // Check for duplicate inputs |
| 110 | + if (vInOutPoints.count(txin.prevout)) |
| 111 | + return state.DoS(100, error("CheckTransaction() : duplicate inputs"), REJECT_INVALID, "bad-txns-inputs-duplicate"); |
| 112 | + |
| 113 | + //duplicate zcspend serials are checked in CheckZerocoinSpend() |
| 114 | + if (!txin.IsZerocoinSpend()) { |
| 115 | + vInOutPoints.insert(txin.prevout); |
| 116 | + } else if (!txin.IsZerocoinPublicSpend()) { |
| 117 | + nZCSpendCount++; |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + if (fZerocoinActive) { |
| 122 | + if (nZCSpendCount > Params().Zerocoin_MaxSpendsPerTransaction()) |
| 123 | + return state.DoS(100, error("CheckTransaction() : there are more zerocoin spends than are allowed in one transaction")); |
| 124 | + |
| 125 | + //require that a zerocoinspend only has inputs that are zerocoins |
| 126 | + if (tx.HasZerocoinSpendInputs()) { |
| 127 | + for (const CTxIn& in : tx.vin) { |
| 128 | + if (!in.IsZerocoinSpend() && !in.IsZerocoinPublicSpend()) |
| 129 | + return state.DoS(100, |
| 130 | + error("CheckTransaction() : zerocoinspend contains inputs that are not zerocoins")); |
| 131 | + } |
| 132 | + |
| 133 | + // Do not require signature verification if this is initial sync and a block over 24 hours old |
| 134 | + bool fVerifySignature = !IsInitialBlockDownload() && (GetTime() - chainActive.Tip()->GetBlockTime() < (60*60*24)); |
| 135 | + if (!CheckZerocoinSpend(tx, fVerifySignature, state, fFakeSerialAttack)) |
| 136 | + return state.DoS(100, error("CheckTransaction() : invalid zerocoin spend")); |
| 137 | + } |
| 138 | + } |
| 139 | + |
| 140 | + if (tx.IsCoinBase()) { |
| 141 | + if (tx.vin[0].scriptSig.size() < 2 || tx.vin[0].scriptSig.size() > 150) |
| 142 | + return state.DoS(100, error("CheckTransaction() : coinbase script size=%d", tx.vin[0].scriptSig.size()), |
| 143 | + REJECT_INVALID, "bad-cb-length"); |
| 144 | + } else if (fZerocoinActive && tx.HasZerocoinSpendInputs()) { |
| 145 | + if (tx.vin.size() < 1) |
| 146 | + return state.DoS(10, error("CheckTransaction() : Zerocoin Spend has less than allowed txin's"), REJECT_INVALID, "bad-zerocoinspend"); |
| 147 | + if (tx.HasZerocoinPublicSpendInputs()) { |
| 148 | + // tx has public zerocoin spend inputs |
| 149 | + if(static_cast<int>(tx.vin.size()) > Params().Zerocoin_MaxPublicSpendsPerTransaction()) |
| 150 | + return state.DoS(10, error("CheckTransaction() : Zerocoin Spend has more than allowed txin's"), REJECT_INVALID, "bad-zerocoinspend"); |
| 151 | + } else { |
| 152 | + // tx has regular zerocoin spend inputs |
| 153 | + if(static_cast<int>(tx.vin.size()) > Params().Zerocoin_MaxSpendsPerTransaction()) |
| 154 | + return state.DoS(10, error("CheckTransaction() : Zerocoin Spend has more than allowed txin's"), REJECT_INVALID, "bad-zerocoinspend"); |
| 155 | + } |
| 156 | + |
| 157 | + } else { |
| 158 | + for (const CTxIn& txin : tx.vin) |
| 159 | + if (txin.prevout.IsNull() && (fZerocoinActive && !txin.IsZerocoinSpend())) |
| 160 | + return state.DoS(10, error("CheckTransaction() : prevout is null"), |
| 161 | + REJECT_INVALID, "bad-txns-prevout-null"); |
| 162 | + } |
| 163 | + |
| 164 | + return true; |
| 165 | +} |
0 commit comments