public override void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block) { if (BlockStake.IsProofOfStake(block)) { // proof of stake invalidates previous inputs // and spends the inputs to new outputs with the // additional stake reward, next calculate the // reward does not exceed the consensus rules var stakeReward = block.Transactions[1].TotalOut - context.Stake.TotalCoinStakeValueIn; var calcStakeReward = nFees + GetProofOfStakeReward(chainedBlock.Height); if (stakeReward > calcStakeReward) { ConsensusErrors.BadCoinstakeAmount.Throw(); } } else { var blockReward = nFees + GetProofOfWorkReward(chainedBlock.Height); if (block.Transactions[0].TotalOut > blockReward) { ConsensusErrors.BadCoinbaseAmount.Throw(); } } }
public virtual void ContextualCheckBlockHeader(ContextInformation context) { Guard.NotNull(context.BestBlock, nameof(context.BestBlock)); BlockHeader header = context.BlockResult.Block.Header; int nHeight = context.BestBlock.Height + 1; // Check proof of work if (header.Bits != context.NextWorkRequired) { ConsensusErrors.BadDiffBits.Throw(); } // Check timestamp against prev if (header.BlockTime <= context.BestBlock.MedianTimePast) { ConsensusErrors.TimeTooOld.Throw(); } // Check timestamp if (header.BlockTime > context.Time + TimeSpan.FromHours(2)) { ConsensusErrors.TimeTooNew.Throw(); } // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: // check for version 2, 3 and 4 upgrades if ((header.Version < 2 && nHeight >= consensusParams.BuriedDeployments[BuriedDeployments.BIP34]) || (header.Version < 3 && nHeight >= consensusParams.BuriedDeployments[BuriedDeployments.BIP66]) || (header.Version < 4 && nHeight >= consensusParams.BuriedDeployments[BuriedDeployments.BIP65])) { ConsensusErrors.BadVersion.Throw(); } }
public override void ContextualCheckBlock(ContextInformation context) { base.ContextualCheckBlock(context); // TODO: fix this validation code //// check proof-of-stake //// Limited duplicity on stake: prevents block flood attack //// Duplicate stake allowed only when there is orphan child block //if (!fReindex && !fImporting && pblock->IsProofOfStake() && setStakeSeen.count(pblock->GetProofOfStake()) && !mapOrphanBlocksByPrev.count(hash)) // return error("ProcessBlock() : duplicate proof-of-stake (%s, %d) for block %s", pblock->GetProofOfStake().first.ToString(), pblock->GetProofOfStake().second, hash.ToString()); //if (!BlockValidator.IsCanonicalBlockSignature(context.BlockResult.Block, false)) //{ // //if (node != null && (int)node.Version >= CANONICAL_BLOCK_SIG_VERSION) // //node.Misbehaving(100); // //return false; //error("ProcessBlock(): bad block signature encoding"); //} //if (!BlockValidator.IsCanonicalBlockSignature(context.BlockResult.Block, true)) //{ // //if (pfrom && pfrom->nVersion >= CANONICAL_BLOCK_SIG_LOW_S_VERSION) // //{ // // pfrom->Misbehaving(100); // // return error("ProcessBlock(): bad block signature encoding (low-s)"); // //} // if (!BlockValidator.EnsureLowS(context.BlockResult.Block.BlockSignatur)) // return false; // error("ProcessBlock(): EnsureLowS failed"); //} }
protected virtual void UpdateCoinView(ContextInformation context, Transaction tx) { ChainedBlock index = context.BlockResult.ChainedBlock; UnspentOutputSet view = context.Set; view.Update(tx, index.Height); }
public virtual void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block) { Money blockReward = nFees + GetBlockSubsidy(chainedBlock.Height); if (block.Transactions[0].TotalOut > blockReward) { ConsensusErrors.BadCoinbaseAmount.Throw(); } }
public override void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler) { // compute and store the stake proofs this.CheckAndComputeStake(context); base.ExecuteBlock(context, taskScheduler); this.stakeChain.Set(context.BlockResult.ChainedBlock.HashBlock, context.Stake.BlockStake); }
public virtual void CheckBlockHeader(ContextInformation context) { if (context.CheckPow && !context.BlockResult.Block.Header.CheckProofOfWork()) { ConsensusErrors.HighHash.Throw(); } context.NextWorkRequired = context.BlockResult.ChainedBlock.GetWorkRequired(context.Consensus); }
public override void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler) { // compute and store the stake proofs this.CheckAndComputeStake(context); base.ExecuteBlock(context, taskScheduler); // TODO: a temporary fix til this methods is fixed in NStratis (this.stakeChain as StakeChainStore).Set(context.BlockResult.ChainedBlock, context.Stake.BlockStake); }
protected override void UpdateCoinView(ContextInformation context, Transaction tx) { UnspentOutputSet view = context.Set; if (tx.IsCoinStake) { context.Stake.TotalCoinStakeValueIn = view.GetValueIn(tx); } base.UpdateCoinView(context, tx); }
private void CheckStakeKernelHash(ContextInformation context, ChainedBlock pindexPrev, uint nBits, ChainedBlock blockFrom, UnspentOutputs txPrev, BlockStake prevBlockStake, OutPoint prevout, uint nTimeTx) { if (IsProtocolV2(pindexPrev.Height + 1)) { this.CheckStakeKernelHashV2(context, pindexPrev, nBits, blockFrom.Header.Time, prevBlockStake, txPrev, prevout, nTimeTx); } else { this.CheckStakeKernelHashV1(); } }
public void CheckKernel(ContextInformation context, ChainedBlock pindexPrev, uint nBits, long nTime, OutPoint prevout, ref long pBlockTime) { var coins = this.coinView.FetchCoinsAsync(new[] { prevout.Hash }).GetAwaiter().GetResult(); if (coins == null || coins.UnspentOutputs.Length != 1) { ConsensusErrors.ReadTxPrevFailed.Throw(); } var prevBlock = chain.GetBlock(coins.BlockHash); var prevUtxo = coins.UnspentOutputs[0]; //var txPrev = trasnactionStore.Get(prevout.Hash); //if (txPrev == null) // return false; //// Read block header //var blockHashPrev = mapStore.GetBlockHash(prevout.Hash); //var block = blockHashPrev == null ? null : blockStore.GetBlock(blockHashPrev); //if (block == null) // return false; if (IsProtocolV3((int)nTime)) { if (IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1)) { ConsensusErrors.InvalidStakeDepth.Throw(); } } else { var nTimeBlockFrom = prevBlock.Header.Time; if (nTimeBlockFrom + this.consensusOptions.StakeMinAge > nTime) { ConsensusErrors.MinAgeViolation.Throw(); } } var prevBlockStake = stakeChain.Get(pindexPrev.HashBlock); if (prevBlockStake == null) { ConsensusErrors.BadStakeBlock.Throw(); } // todo: check this unclear logic //if (pBlockTime) // pBlockTime = block.Header.Time; this.CheckStakeKernelHash(context, pindexPrev, nBits, prevBlock, prevUtxo, prevBlockStake, prevout, (uint)nTime); }
public override void CheckBlockHeader(ContextInformation context) { context.SetStake(); if (context.Stake.BlockStake.IsProofOfWork()) { if (context.CheckPow && !context.BlockResult.Block.Header.CheckProofOfWork()) { ConsensusErrors.HighHash.Throw(); } } context.NextWorkRequired = StakeValidator.GetNextTargetRequired(this.stakeChain, context.BlockResult.ChainedBlock.Previous, context.Consensus, context.Stake.BlockStake.IsProofOfStake()); }
public override void ContextualCheckBlockHeader(ContextInformation context) { base.ContextualCheckBlockHeader(context); var chainedBlock = context.BlockResult.ChainedBlock; if (!StakeValidator.IsProtocolV3((int)chainedBlock.Header.Time)) { if (chainedBlock.Header.Version > BlockHeader.CURRENT_VERSION) { ConsensusErrors.BadVersion.Throw(); } } if (StakeValidator.IsProtocolV2(chainedBlock.Height) && chainedBlock.Header.Version < 7) { ConsensusErrors.BadVersion.Throw(); } else if (!StakeValidator.IsProtocolV2(chainedBlock.Height) && chainedBlock.Header.Version > 6) { ConsensusErrors.BadVersion.Throw(); } if (context.Stake.BlockStake.IsProofOfWork() && chainedBlock.Height > this.ConsensusParams.LastPOWBlock) { ConsensusErrors.ProofOfWorkTooHeigh.Throw(); } // Check coinbase timestamp if (chainedBlock.Header.Time > FutureDrift(context.BlockResult.Block.Transactions[0].Time, chainedBlock.Height)) { ConsensusErrors.TimeTooNew.Throw(); } // Check coinstake timestamp if (context.Stake.BlockStake.IsProofOfStake() && !PosConsensusValidator.CheckCoinStakeTimestamp(chainedBlock.Height, chainedBlock.Header.Time, context.BlockResult.Block.Transactions[1].Time)) { ConsensusErrors.StakeTimeViolation.Throw(); } // Check timestamp against prev if (chainedBlock.Header.Time <= StakeValidator.GetPastTimeLimit(chainedBlock.Previous) || FutureDrift(chainedBlock.Header.Time, chainedBlock.Height) < chainedBlock.Previous.Header.Time) { ConsensusErrors.BlockTimestampTooEarly.Throw(); } }
public override void CheckBlock(ContextInformation context) { base.CheckBlock(context); var block = context.BlockResult.Block; // Check timestamp if (block.Header.Time > FutureDriftV2(DateTime.UtcNow.Ticks)) { ConsensusErrors.BlockTimestampTooFar.Throw(); } if (BlockStake.IsProofOfStake(block)) { // Coinbase output should be empty if proof-of-stake block if (block.Transactions[0].Outputs.Count != 1 || !block.Transactions[0].Outputs[0].IsEmpty) { ConsensusErrors.BadStakeBlock.Throw(); } // Second transaction must be coinstake, the rest must not be if (!block.Transactions[1].IsCoinStake) { ConsensusErrors.BadStakeBlock.Throw(); } if (block.Transactions.Skip(2).Any(t => t.IsCoinStake)) { ConsensusErrors.BadMultipleCoinstake.Throw(); } } // Check proof-of-stake block signature if (!CheckBlockSignature(block)) { ConsensusErrors.BadBlockSignature.Throw(); } // Check transactions foreach (var transaction in block.Transactions) { // check transaction timestamp if (block.Header.Time < transaction.Time) { ConsensusErrors.BlockTimeBeforeTrx.Throw(); } } }
public void CheckProofOfStake(ContextInformation context, ChainedBlock pindexPrev, BlockStake prevBlockStake, Transaction tx, uint nBits) { if (!tx.IsCoinStake) { ConsensusErrors.NonCoinstake.Throw(); } // Kernel (input 0) must match the stake hash target per coin age (nBits) var txIn = tx.Inputs[0]; // First try finding the previous transaction in database var coins = coinView.FetchCoinsAsync(new[] { txIn.PrevOut.Hash }).GetAwaiter().GetResult(); if (coins == null || coins.UnspentOutputs.Length != 1) { ConsensusErrors.ReadTxPrevFailed.Throw(); } var prevBlock = chain.GetBlock(coins.BlockHash); var prevUtxo = coins.UnspentOutputs[0]; // Verify signature if (!this.VerifySignature(prevUtxo, tx, 0, ScriptVerify.None)) { ConsensusErrors.CoinstakeVerifySignatureFailed.Throw(); } // Min age requirement if (IsProtocolV3((int)tx.Time)) { if (IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1)) { ConsensusErrors.InvalidStakeDepth.Throw(); } } else { var nTimeBlockFrom = prevBlock.Header.Time; if (nTimeBlockFrom + this.consensusOptions.StakeMinAge > tx.Time) { ConsensusErrors.MinAgeViolation.Throw(); } } this.CheckStakeKernelHash(context, pindexPrev, nBits, prevBlock, prevUtxo, prevBlockStake, txIn.PrevOut, tx.Time); }
public void CheckAndComputeStake(ContextInformation context) { var pindex = context.BlockResult.ChainedBlock; var block = context.BlockResult.Block; var blockStake = context.Stake.BlockStake; // Verify hash target and signature of coinstake tx if (BlockStake.IsProofOfStake(block)) { var pindexPrev = pindex.Previous; var prevBlockStake = this.stakeChain.Get(pindexPrev.HashBlock); if (prevBlockStake == null) { ConsensusErrors.PrevStakeNull.Throw(); } this.stakeValidator.CheckProofOfStake(context, pindexPrev, prevBlockStake, block.Transactions[1], pindex.Header.Bits.ToCompact()); } // PoW is checked in CheckBlock() if (BlockStake.IsProofOfWork(block)) { context.Stake.HashProofOfStake = pindex.Header.GetPoWHash(); } // TODO: is this the same as chain work? // compute chain trust score //pindexNew.nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust(); // compute stake entropy bit for stake modifier if (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit())) { ConsensusErrors.SetStakeEntropyBitFailed.Throw(); } // Record proof hash value blockStake.HashProof = context.Stake.HashProofOfStake; // compute stake modifier this.stakeValidator.ComputeStakeModifier(this.chain, pindex, blockStake); }
// Stratis kernel protocol // coinstake must meet hash target according to the protocol: // kernel (input 0) must meet the formula // hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime) < bnTarget * nWeight // this ensures that the chance of getting a coinstake is proportional to the // amount of coins one owns. // The reason this hash is chosen is the following: // nStakeModifier: scrambles computation to make it very difficult to precompute // future proof-of-stake // txPrev.block.nTime: prevent nodes from guessing a good timestamp to // generate transaction for future advantage, // obsolete since v3 // txPrev.nTime: slightly scrambles computation // txPrev.vout.hash: hash of txPrev, to reduce the chance of nodes // generating coinstake at the same time // txPrev.vout.n: output number of txPrev, to reduce the chance of nodes // generating coinstake at the same time // nTime: current timestamp // block/tx hash should not be used here as they can be generated in vast // quantities so as to generate blocks faster, degrading the system back into // a proof-of-work situation. // private void CheckStakeKernelHashV2(ContextInformation context, ChainedBlock pindexPrev, uint nBits, uint nTimeBlockFrom, BlockStake prevBlockStake, UnspentOutputs txPrev, OutPoint prevout, uint nTimeTx) { if (nTimeTx < txPrev.Time) { ConsensusErrors.StakeTimeViolation.Throw(); } // Base target var bnTarget = new Target(nBits).ToBigInteger(); // Weighted target var nValueIn = txPrev._Outputs[prevout.N].Value.Satoshi; var bnWeight = BigInteger.ValueOf(nValueIn); bnTarget = bnTarget.Multiply(bnWeight); // todo: investigate this issue, is the convertion to uint256 similar to the c++ implementation //context.Stake.TargetProofOfStake = Target.ToUInt256(bnTarget,); var nStakeModifier = prevBlockStake.StakeModifier; //pindexPrev.Header.BlockStake.StakeModifier; uint256 bnStakeModifierV2 = prevBlockStake.StakeModifierV2; //pindexPrev.Header.BlockStake.StakeModifierV2; int nStakeModifierHeight = pindexPrev.Height; var nStakeModifierTime = pindexPrev.Header.Time; // Calculate hash using (var ms = new MemoryStream()) { var serializer = new BitcoinStream(ms, true); if (IsProtocolV3((int)nTimeTx)) { serializer.ReadWrite(bnStakeModifierV2); } else { serializer.ReadWrite(nStakeModifier); serializer.ReadWrite(nTimeBlockFrom); } serializer.ReadWrite(txPrev.Time); serializer.ReadWrite(prevout.Hash); serializer.ReadWrite(prevout.N); serializer.ReadWrite(nTimeTx); context.Stake.HashProofOfStake = Hashes.Hash256(ms.ToArray()); } //LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n", // nStakeModifier, nStakeModifierHeight, // DateTimeStrFormat(nStakeModifierTime), // DateTimeStrFormat(nTimeBlockFrom)); //LogPrintf("CheckStakeKernelHash() : check modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", // nStakeModifier, // nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx, // hashProofOfStake.ToString()); // Now check if proof-of-stake hash meets target protocol var hashProofOfStakeTarget = new BigInteger(context.Stake.HashProofOfStake.ToBytes(false)); if (hashProofOfStakeTarget.CompareTo(bnTarget) > 0) { ConsensusErrors.StakeHashInvalidTarget.Throw(); } // if (fDebug && !fPrintProofOfStake) // { // LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n", // nStakeModifier, nStakeModifierHeight, // DateTimeStrFormat(nStakeModifierTime), // DateTimeStrFormat(nTimeBlockFrom)); // LogPrintf("CheckStakeKernelHash() : pass modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", // nStakeModifier, // nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx, // hashProofOfStake.ToString()); // } }
// Stratis kernel protocol // coinstake must meet hash target according to the protocol: // kernel (input 0) must meet the formula // hash(nStakeModifier + txPrev.block.nTime + txPrev.nTime + txPrev.vout.hash + txPrev.vout.n + nTime) < bnTarget * nWeight // this ensures that the chance of getting a coinstake is proportional to the // amount of coins one owns. // The reason this hash is chosen is the following: // nStakeModifier: scrambles computation to make it very difficult to precompute // future proof-of-stake // txPrev.block.nTime: prevent nodes from guessing a good timestamp to // generate transaction for future advantage, // obsolete since v3 // txPrev.nTime: slightly scrambles computation // txPrev.vout.hash: hash of txPrev, to reduce the chance of nodes // generating coinstake at the same time // txPrev.vout.n: output number of txPrev, to reduce the chance of nodes // generating coinstake at the same time // nTime: current timestamp // block/tx hash should not be used here as they can be generated in vast // quantities so as to generate blocks faster, degrading the system back into // a proof-of-work situation. // private void CheckStakeKernelHashV2(ContextInformation context, ChainedBlock pindexPrev, uint nBits, uint nTimeBlockFrom, BlockStake prevBlockStake, UnspentOutputs txPrev, OutPoint prevout, uint nTimeTx) { if (nTimeTx < txPrev.Time) { ConsensusErrors.StakeTimeViolation.Throw(); } // Base target var bnTarget = new Target(nBits).ToBigInteger(); // TODO: Investigate: // The POS protocol should probably put a limit on the max amount that can be staked // not a hard limit but a limit that allow any amount to be staked with a max weight value. // the max weight should not exceed the max uint256 array size (array siez = 32) // Weighted target var nValueIn = txPrev._Outputs[prevout.N].Value.Satoshi; var bnWeight = BigInteger.ValueOf(nValueIn); bnTarget = bnTarget.Multiply(bnWeight); context.Stake.TargetProofOfStake = ToUInt256(bnTarget); var nStakeModifier = prevBlockStake.StakeModifier; //pindexPrev.Header.BlockStake.StakeModifier; uint256 bnStakeModifierV2 = prevBlockStake.StakeModifierV2; //pindexPrev.Header.BlockStake.StakeModifierV2; int nStakeModifierHeight = pindexPrev.Height; var nStakeModifierTime = pindexPrev.Header.Time; // Calculate hash using (var ms = new MemoryStream()) { var serializer = new BitcoinStream(ms, true); if (IsProtocolV3((int)nTimeTx)) { serializer.ReadWrite(bnStakeModifierV2); } else { serializer.ReadWrite(nStakeModifier); serializer.ReadWrite(nTimeBlockFrom); } serializer.ReadWrite(txPrev.Time); serializer.ReadWrite(prevout.Hash); serializer.ReadWrite(prevout.N); serializer.ReadWrite(nTimeTx); context.Stake.HashProofOfStake = Hashes.Hash256(ms.ToArray()); } //LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n", // nStakeModifier, nStakeModifierHeight, // DateTimeStrFormat(nStakeModifierTime), // DateTimeStrFormat(nTimeBlockFrom)); //LogPrintf("CheckStakeKernelHash() : check modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", // nStakeModifier, // nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx, // hashProofOfStake.ToString()); // Now check if proof-of-stake hash meets target protocol var hashProofOfStakeTarget = new BigInteger(1, context.Stake.HashProofOfStake.ToBytes(false)); if (hashProofOfStakeTarget.CompareTo(bnTarget) > 0) { ConsensusErrors.StakeHashInvalidTarget.Throw(); } // if (fDebug && !fPrintProofOfStake) // { // LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from timestamp=%s\n", // nStakeModifier, nStakeModifierHeight, // DateTimeStrFormat(nStakeModifierTime), // DateTimeStrFormat(nTimeBlockFrom)); // LogPrintf("CheckStakeKernelHash() : pass modifier=0x%016x nTimeBlockFrom=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", // nStakeModifier, // nTimeBlockFrom, txPrev.nTime, prevout.n, nTimeTx, // hashProofOfStake.ToString()); // } }
public void AcceptBlock(ContextInformation context) { using (this.watch.Start(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o))) { // check that the current block has not been reorged // catching a reorg at this point will not require a rewind if (context.BlockResult.Block.Header.HashPrevBlock != this.Tip.HashBlock) { ConsensusErrors.InvalidPrevTip.Throw(); // reorg } // build the next block in the chain of headers // the chain header is most likely already created by // one of the peers so after we create a new chained block (mainly for validation) // we ask the chain headers for its version (also to prevent mempry leaks) context.BlockResult.ChainedBlock = new ChainedBlock(context.BlockResult.Block.Header, context.BlockResult.Block.Header.GetHash(), this.Tip); //Liberate from memory the block created above if possible context.BlockResult.ChainedBlock = this.Chain.GetBlock(context.BlockResult.ChainedBlock.HashBlock) ?? context.BlockResult.ChainedBlock; context.SetBestBlock(); // == validation flow == // check the block hedaer is correct this.Validator.CheckBlockHeader(context); this.Validator.ContextualCheckBlockHeader(context); // calculate the consensus flags // and check they are valid context.Flags = this.NodeDeployments.GetFlags(context.BlockResult.ChainedBlock); this.Validator.ContextualCheckBlock(context); // check the block itself this.Validator.CheckBlock(context); } if (context.OnlyCheck) { return; } // load the UTXO set of the current block // UTXO may be loaded form cache or from disk // the UTXO set are stored in the context context.Set = new UnspentOutputSet(); using (this.watch.Start(o => this.Validator.PerformanceCounter.AddUTXOFetchingTime(o))) { var ids = GetIdsToFetch(context.BlockResult.Block, context.Flags.EnforceBIP30); var coins = this.UTXOSet.FetchCoinsAsync(ids).GetAwaiter().GetResult(); context.Set.SetCoins(coins); } // attempt to load in to cach the // next set of UTXO to be validated // the task is not awaited so will not // stall main validation process this.TryPrefetchAsync(context.Flags); // validate the UTXO set are correctly spent using (this.watch.Start(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o))) { this.Validator.ExecuteBlock(context, null); } // persist the changes to the coinview // this will likely only be sotred in mempry // unless the coinview trashold is reached this.UTXOSet.SaveChangesAsync(context.Set.GetCoins(this.UTXOSet), null, this.Tip.HashBlock, context.BlockResult.ChainedBlock.HashBlock); // set the new tip. this.Tip = context.BlockResult.ChainedBlock; }
public virtual void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler) { Block block = context.BlockResult.Block; ChainedBlock index = context.BlockResult.ChainedBlock; ConsensusFlags flags = context.Flags; UnspentOutputSet view = context.Set; 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 && !tx.IsCoinStake) { 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 > this.consensusOptions.MAX_BLOCK_SIGOPS_COST) { ConsensusErrors.BadBlockSigOps.Throw(); } if (!tx.IsCoinBase && !tx.IsCoinStake) { CheckInputs(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); } } if (tx.IsCoinStake) { context.Stake.TotalCoinStakeValueIn = view.GetValueIn(tx); } view.Update(tx, index.Height); } this.CheckBlockReward(context, nFees, index, block); var passed = checkInputs.All(c => c.GetAwaiter().GetResult()); if (!passed) { ConsensusErrors.BadTransactionScriptError.Throw(); } }
public virtual void ContextualCheckBlock(ContextInformation context) { var block = context.BlockResult.Block; var consensusFlags = context.Flags; int nHeight = context.BestBlock == null ? 0 : context.BestBlock.Height + 1; // Start enforcing BIP113 (Median Time Past) using versionbits logic. var nLockTimeCutoff = consensusFlags.LockTimeFlags.HasFlag(LockTimeFlags.MedianTimePast) ? context.BestBlock.MedianTimePast : block.Header.BlockTime; // Check that all transactions are finalized foreach (var transaction in block.Transactions) { if (!transaction.IsFinal(nLockTimeCutoff, nHeight)) { ConsensusErrors.BadTransactionNonFinal.Throw(); } } // Enforce rule that the coinbase starts with serialized block height if (consensusFlags.EnforceBIP34) { Script expect = new Script(Op.GetPushOp(nHeight)); Script actual = block.Transactions[0].Inputs[0].ScriptSig; if (!StartWith(actual.ToBytes(true), expect.ToBytes(true))) { ConsensusErrors.BadCoinbaseHeight.Throw(); } } // Validation for witness commitments. // * We compute the witness hash (which is the hash including witnesses) of all the block's transactions, except the // coinbase (where 0x0000....0000 is used instead). // * The coinbase scriptWitness is a stack of a single 32-byte vector, containing a witness nonce (unconstrained). // * We build a merkle tree with all those witness hashes as leaves (similar to the hashMerkleRoot in the block header). // * There must be at least one output whose scriptPubKey is a single 36-byte push, the first 4 bytes of which are // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness nonce). In case there are // multiple, the last one is used. bool fHaveWitness = false; if (consensusFlags.ScriptFlags.HasFlag(ScriptVerify.Witness)) { int commitpos = GetWitnessCommitmentIndex(block); if (commitpos != -1) { bool malleated = false; uint256 hashWitness = BlockWitnessMerkleRoot(block, ref malleated); // The malleation check is ignored; as the transaction tree itself // already does not permit it, it is impossible to trigger in the // witness tree. var witness = block.Transactions[0].Inputs[0].WitScript; if (witness.PushCount != 1 || witness.Pushes.First().Length != 32) { ConsensusErrors.BadWitnessNonceSize.Throw(); } byte[] hashed = new byte[64]; Buffer.BlockCopy(hashWitness.ToBytes(), 0, hashed, 0, 32); Buffer.BlockCopy(witness.Pushes.First(), 0, hashed, 32, 32); hashWitness = Hashes.Hash256(hashed); if (!EqualsArray(hashWitness.ToBytes(), block.Transactions[0].Outputs[commitpos].ScriptPubKey.ToBytes(true).Skip(6).ToArray(), 32)) { ConsensusErrors.BadWitnessMerkleMatch.Throw(); } fHaveWitness = true; } } if (!fHaveWitness) { for (var i = 0; i < block.Transactions.Count; i++) { if (block.Transactions[i].HasWitness) { ConsensusErrors.UnexpectedWitness.Throw(); } } } // After the coinbase witness nonce and commitment are verified, // we can check if the block weight passes (before we've checked the // coinbase witness, it would be possible for the weight to be too // large by filling up the coinbase witness, which doesn't change // the block hash, so we couldn't mark the block as permanently // failed). if (GetBlockWeight(block) > this.consensusOptions.MAX_BLOCK_WEIGHT) { ConsensusErrors.BadCoinbaseHeight.Throw(); } }
public virtual void CheckBlock(ContextInformation context) { Block block = context.BlockResult.Block; bool mutated = false; uint256 hashMerkleRoot2 = BlockMerkleRoot(block, ref mutated); if (context.CheckMerkleRoot && block.Header.HashMerkleRoot != hashMerkleRoot2) { ConsensusErrors.BadMerkleRoot.Throw(); } // Check for merkle tree malleability (CVE-2012-2459): repeating sequences // of transactions in a block without affecting the merkle root of a block, // while still invalidating it. if (mutated) { ConsensusErrors.BadTransactionDuplicate.Throw(); } // All potential-corruption validation must be done before we do any // transaction validation, as otherwise we may mark the header as invalid // because we receive the wrong transactions for it. // Note that witness malleability is checked in ContextualCheckBlock, so no // checks that use witness data may be performed here. // Size limits if (block.Transactions.Count == 0 || block.Transactions.Count > this.consensusOptions.MAX_BLOCK_BASE_SIZE || GetSize(block, TransactionOptions.None) > this.consensusOptions.MAX_BLOCK_BASE_SIZE) { ConsensusErrors.BadBlockLength.Throw(); } // First transaction must be coinbase, the rest must not be if (block.Transactions.Count == 0 || !block.Transactions[0].IsCoinBase) { ConsensusErrors.BadCoinbaseMissing.Throw(); } for (var i = 1; i < block.Transactions.Count; i++) { if (block.Transactions[i].IsCoinBase) { ConsensusErrors.BadMultipleCoinbase.Throw(); } } // Check transactions foreach (var tx in block.Transactions) { CheckTransaction(tx); } long nSigOps = 0; foreach (var tx in block.Transactions) { nSigOps += GetLegacySigOpCount(tx); } if (nSigOps * this.consensusOptions.WITNESS_SCALE_FACTOR > this.consensusOptions.MAX_BLOCK_SIGOPS_COST) { ConsensusErrors.BadBlockSigOps.Throw(); } }