public BackgroundCommiterCoinView(CoinView inner) { if (inner == null) { throw new ArgumentNullException("inner"); } _Inner = inner; _InnerCommitable = new CommitableCoinView(Inner); _Commitable = new CommitableCoinView(_InnerCommitable); FlushPeriod = TimeSpan.FromSeconds(5); }
private void CheckIntputs(Transaction tx, CommitableCoinView inputs, int nSpendHeight) { if (!inputs.HaveInputs(tx)) { ConsensusErrors.BadTransactionMissingInput.Throw(); } Money nValueIn = Money.Zero; Money nFees = Money.Zero; for (int i = 0; i < tx.Inputs.Count; i++) { var prevout = tx.Inputs[i].PrevOut; var coins = inputs.AccessCoins(prevout.Hash); // If prev is coinbase, check that it's matured if (coins.IsCoinbase) { if (nSpendHeight - coins.Height < COINBASE_MATURITY) { ConsensusErrors.BadTransactionPrematureCoinbaseSpending.Throw(); } } // Check for negative or overflow input values nValueIn += coins.TryGetOutput(prevout.N).Value; if (!MoneyRange(coins.TryGetOutput(prevout.N).Value) || !MoneyRange(nValueIn)) { ConsensusErrors.BadTransactionInputValueOutOfRange.Throw(); } } if (nValueIn < tx.TotalOut) { ConsensusErrors.BadTransactionInBelowOut.Throw(); } // Tally transaction fees Money nTxFee = nValueIn - tx.TotalOut; if (nTxFee < 0) { ConsensusErrors.BadTransactionNegativeFee.Throw(); } nFees += nTxFee; if (!MoneyRange(nFees)) { ConsensusErrors.BadTransactionFeeOutOfRange.Throw(); } }
private uint GetP2SHSigOpCount(Transaction tx, CommitableCoinView inputs) { if (tx.IsCoinBase) { return(0); } uint nSigOps = 0; for (uint i = 0; i < tx.Inputs.Count; i++) { var prevout = inputs.GetOutputFor(tx.Inputs[i]); if (prevout.ScriptPubKey.IsPayToScriptHash) { nSigOps += prevout.ScriptPubKey.GetSigOpCount(tx.Inputs[i].ScriptSig); } } return(nSigOps); }
private long GetTransactionSigOpCost(Transaction tx, CommitableCoinView inputs, ConsensusFlags flags) { long nSigOps = GetLegacySigOpCount(tx) * WITNESS_SCALE_FACTOR; if (tx.IsCoinBase) { return(nSigOps); } if (flags.ScriptFlags.HasFlag(ScriptVerify.P2SH)) { nSigOps += GetP2SHSigOpCount(tx, inputs) * WITNESS_SCALE_FACTOR; } for (var i = 0; i < tx.Inputs.Count; i++) { var prevout = inputs.GetOutputFor(tx.Inputs[i]); nSigOps += CountWitnessSigOps(tx.Inputs[i].ScriptSig, prevout.ScriptPubKey, tx.Inputs[i].WitScript, flags); } return(nSigOps); }
/// <summary> /// Pull blocks, validate them and update the UTXO Set /// </summary> /// <param name="utxo">UTXO Set</param> /// <param name="puller">Block source</param> /// <returns>Stream of validated blocks</returns> public IEnumerable <Block> Run(CoinViewStack utxoStack, BlockPuller puller) { var utxo = utxoStack.Top; var cache = utxoStack.Find <CacheCoinView>(); var lookaheadPuller = puller as ILookaheadBlockPuller; puller.SetLocation(utxo.Tip); ThresholdConditionCache bip9 = new ThresholdConditionCache(_ConsensusParams); StopWatch watch = new StopWatch(); bool rejected = false; while (true) { Block block = null; while (true) { rejected = false; try { using (watch.Start(o => PerformanceCounter.AddBlockFetchingTime(o))) { block = puller.NextBlock(); } ChainedBlock next; ContextInformation context; ConsensusFlags flags; using (watch.Start(o => PerformanceCounter.AddBlockProcessingTime(o))) { CheckBlockHeader(block.Header); next = new ChainedBlock(block.Header, block.Header.GetHash(), utxo.Tip); context = new ContextInformation(next, this._ConsensusParams); ContextualCheckBlockHeader(block.Header, context); var states = bip9.GetStates(utxo.Tip); flags = new ConsensusFlags(next, states, _ConsensusParams); ContextualCheckBlock(block, flags, context); CheckBlock(block); } var commitable = new CommitableCoinView(next, utxo); using (watch.Start(o => PerformanceCounter.AddUTXOFetchingTime(o))) { commitable.FetchCoins(GetIdsToFetch(block, flags.EnforceBIP30)); } commitable.SetInner(NullCoinView.Instance); Task prefetching = GetPrefetchingTask(cache, lookaheadPuller, flags); using (watch.Start(o => PerformanceCounter.AddBlockProcessingTime(o))) { ExecuteBlock(block, next, flags, commitable, null); } using (watch.Start(o => PerformanceCounter.AddUTXOFetchingTime(o))) { prefetching.Wait(); commitable.Commit(utxo); } } catch (ConsensusErrorException ex) { rejected = true; if (ex.ConsensusError == ConsensusErrors.TimeTooNew) { puller.Reject(block, RejectionMode.Temporary); } else { puller.Reject(block, RejectionMode.Permanent); } } if (!rejected) { yield return(block); } } } }
public void ExecuteBlock(Block block, ChainedBlock index, ConsensusFlags flags, CommitableCoinView view, TaskScheduler taskScheduler) { PerformanceCounter.AddProcessedBlocks(1); taskScheduler = taskScheduler ?? TaskScheduler.Default; if (flags.EnforceBIP30) { foreach (var tx in block.Transactions) { var coins = view.AccessCoins(tx.GetHash()); if (coins != null && !coins.IsPrunable) { ConsensusErrors.BadTransactionBIP30.Throw(); } } } long nSigOpsCost = 0; Money nFees = Money.Zero; List <Task <bool> > checkInputs = new List <Task <bool> >(); for (int i = 0; i < block.Transactions.Count; i++) { PerformanceCounter.AddProcessedTransactions(1); var tx = block.Transactions[i]; if (!tx.IsCoinBase) { int[] prevheights; if (!view.HaveInputs(tx)) { ConsensusErrors.BadTransactionMissingInput.Throw(); } prevheights = new int[tx.Inputs.Count]; // Check that transaction is BIP68 final // BIP68 lock checks (as opposed to nLockTime checks) must // be in ConnectBlock because they require the UTXO set for (var j = 0; j < tx.Inputs.Count; j++) { prevheights[j] = (int)view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height; } if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags)) { ConsensusErrors.BadTransactionNonFinal.Throw(); } } // GetTransactionSigOpCost counts 3 types of sigops: // * legacy (always) // * p2sh (when P2SH enabled in flags and excludes coinbase) // * witness (when witness enabled in flags and excludes coinbase) nSigOpsCost += GetTransactionSigOpCost(tx, view, flags); if (nSigOpsCost > MAX_BLOCK_SIGOPS_COST) { ConsensusErrors.BadBlockSigOps.Throw(); } if (!tx.IsCoinBase) { CheckIntputs(tx, view, index.Height); nFees += view.GetValueIn(tx) - tx.TotalOut; int ii = i; var localTx = tx; PrecomputedTransactionData txData = new PrecomputedTransactionData(tx); for (int iInput = 0; iInput < tx.Inputs.Count; iInput++) { PerformanceCounter.AddProcessedInputs(1); var input = tx.Inputs[iInput]; int iiIntput = iInput; var txout = view.GetOutputFor(input); var checkInput = new Task <bool>(() => { if (UseConsensusLib) { Script.BitcoinConsensusError error; return(Script.VerifyScriptConsensus(txout.ScriptPubKey, tx, (uint)iiIntput, flags.ScriptFlags, out error)); } else { var checker = new TransactionChecker(tx, iiIntput, txout.Value, txData); var ctx = new ScriptEvaluationContext(); ctx.ScriptVerify = flags.ScriptFlags; return(ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker)); } }); checkInput.Start(taskScheduler); checkInputs.Add(checkInput); } } view.Update(tx, index.Height); } Money blockReward = nFees + GetBlockSubsidy(index.Height); if (block.Transactions[0].TotalOut > blockReward) { ConsensusErrors.BadCoinbaseAmount.Throw(); } var passed = checkInputs.All(c => c.GetAwaiter().GetResult()); if (!passed) { ConsensusErrors.BadTransactionScriptError.Throw(); } }