/// <inheritdoc /> protected override void CheckBlockReward(ContextInformation context, Money fees, int height, Block block) { this.logger.LogTrace("({0}:{1},{2}:'{3}')", nameof(fees), fees, nameof(height), height); if (BlockStake.IsProofOfStake(block)) { Money stakeReward = block.Transactions[1].TotalOut - context.Stake.TotalCoinStakeValueIn; Money calcStakeReward = fees + this.GetProofOfStakeReward(height); this.logger.LogTrace("Block stake reward is {0}, calculated reward is {1}.", stakeReward, calcStakeReward); if (stakeReward > calcStakeReward) { this.logger.LogTrace("(-)[BAD_COINSTAKE_AMOUNT]"); ConsensusErrors.BadCoinstakeAmount.Throw(); } } else { Money blockReward = fees + this.GetProofOfWorkReward(height); this.logger.LogTrace("Block reward is {0}, calculated reward is {1}.", block.Transactions[0].TotalOut, blockReward); if (block.Transactions[0].TotalOut > blockReward) { this.logger.LogTrace("(-)[BAD_COINBASE_AMOUNT]"); ConsensusErrors.BadCoinbaseAmount.Throw(); } } this.logger.LogTrace("(-)"); }
protected virtual void UpdateCoinView(ContextInformation context, Transaction tx) { ChainedBlock index = context.BlockResult.ChainedBlock; UnspentOutputSet view = context.Set; view.Update(tx, index.Height); }
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 + this.GetProofOfStakeReward(chainedBlock.Height); if (stakeReward > calcStakeReward) { ConsensusErrors.BadCoinstakeAmount.Throw(); } } else { var blockReward = nFees + this.GetProofOfWorkReward(chainedBlock.Height); if (block.Transactions[0].TotalOut > blockReward) { ConsensusErrors.BadCoinbaseAmount.Throw(); } } }
public override void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block) { this.logger.LogTrace("({0}:{1},{2}:'{3}')", nameof(nFees), nFees, nameof(chainedBlock), chainedBlock); 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 Money stakeReward = block.Transactions[1].TotalOut - context.Stake.TotalCoinStakeValueIn; Money calcStakeReward = nFees + this.GetProofOfStakeReward(chainedBlock.Height); this.logger.LogTrace("Block stake reward is {0}, calculated reward is {1}.", stakeReward, calcStakeReward); if (stakeReward > calcStakeReward) { this.logger.LogTrace("(-)[BAD_COINSTAKE_AMOUNT]"); ConsensusErrors.BadCoinstakeAmount.Throw(); } } else { Money blockReward = nFees + this.GetProofOfWorkReward(chainedBlock.Height); this.logger.LogTrace("Block reward is {0}, calculated reward is {1}.", block.Transactions[0].TotalOut, blockReward); if (block.Transactions[0].TotalOut > blockReward) { this.logger.LogTrace("(-)[BAD_COINBASE_AMOUNT]"); ConsensusErrors.BadCoinbaseAmount.Throw(); } } this.logger.LogTrace("(-)"); }
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 >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP34]) || (header.Version < 3 && nHeight >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP66]) || (header.Version < 4 && nHeight >= this.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"); //} }
public override void CheckBlock(ContextInformation context) { this.logger.LogTrace("()"); base.CheckBlock(context); Block block = context.BlockResult.Block; // Check timestamp. if (block.Header.Time > this.FutureDrift(this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp())) { this.logger.LogTrace("(-)[TIME_TOO_FAR]"); 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) { this.logger.LogTrace("(-)[COINBASE_NOT_EMPTY]"); ConsensusErrors.BadStakeBlock.Throw(); } // Second transaction must be coinstake, the rest must not be. if (!block.Transactions[1].IsCoinStake) { this.logger.LogTrace("(-)[NO_COINSTAKE]"); ConsensusErrors.BadStakeBlock.Throw(); } if (block.Transactions.Skip(2).Any(t => t.IsCoinStake)) { this.logger.LogTrace("(-)[MULTIPLE_COINSTAKE]"); ConsensusErrors.BadMultipleCoinstake.Throw(); } } // Check proof-of-stake block signature. if (!CheckBlockSignature(block)) { this.logger.LogTrace("(-)[BAD_SIGNATURE]"); ConsensusErrors.BadBlockSignature.Throw(); } // Check transactions. foreach (Transaction transaction in block.Transactions) { // Check transaction timestamp. if (block.Header.Time < transaction.Time) { this.logger.LogTrace("Block contains transaction with timestamp {0}, which is greater than block's timestamp {1}.", transaction.Time, block.Header.Time); this.logger.LogTrace("(-)[TX_TIME_MISMATCH]"); ConsensusErrors.BlockTimeBeforeTrx.Throw(); } } this.logger.LogTrace("(-)[OK]"); }
// Stratis kernel protocol // coinstake must meet hash target according to the protocol: // kernel (input 0) must meet the formula // hash(stakeModifierV2 + stakingCoins.Time + prevout.Hash + prevout.N + transactionTime) < target * weight // 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: // stakeModifierV2: Scrambles computation to make it very difficult to precompute future proof-of-stake. // stakingCoins.Time: Time of the coinstake UTXO. Slightly scrambles computation. // prevout.Hash: Hash of stakingCoins UTXO, to reduce the chance of nodes generating coinstake at the same time. // prevout.N: Output number of stakingCoins UTXO, to reduce the chance of nodes generating coinstake at the same time. // transactionTime: Timestamp of the coinstake transaction. // Block or transaction 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 CheckStakeKernelHash(ContextInformation context, ChainedBlock prevChainedBlock, uint headerBits, uint prevBlockTime, BlockStake prevBlockStake, UnspentOutputs stakingCoins, OutPoint prevout, uint transactionTime) { this.logger.LogTrace("({0}:'{1}/{2}',{3}:{4:X},{5}:{6},{7}.{8}:'{9}',{10}:'{11}/{12}',{13}:'{14}',{15}:{16})", nameof(prevChainedBlock), prevChainedBlock.HashBlock, prevChainedBlock.Height, nameof(headerBits), headerBits, nameof(prevBlockTime), prevBlockTime, nameof(prevBlockStake), nameof(prevBlockStake.HashProof), prevBlockStake.HashProof, nameof(stakingCoins), stakingCoins.TransactionId, stakingCoins.Height, nameof(prevout), prevout, nameof(transactionTime), transactionTime); if (transactionTime < stakingCoins.Time) { this.logger.LogTrace("Coinstake transaction timestamp {0} is lower than its own UTXO timestamp {1}.", transactionTime, stakingCoins.Time); this.logger.LogTrace("(-)[BAD_STAKE_TIME]"); ConsensusErrors.StakeTimeViolation.Throw(); } // Base target. BigInteger target = new Target(headerBits).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 size = 32). // Weighted target. long valueIn = stakingCoins._Outputs[prevout.N].Value.Satoshi; BigInteger weight = BigInteger.ValueOf(valueIn); BigInteger weightedTarget = target.Multiply(weight); context.Stake.TargetProofOfStake = ToUInt256(weightedTarget); this.logger.LogTrace("POS target is '{0}', weighted target for {1} coins is '{2}'.", ToUInt256(target), valueIn, context.Stake.TargetProofOfStake); uint256 stakeModifierV2 = prevBlockStake.StakeModifierV2; // Calculate hash. using (var ms = new MemoryStream()) { var serializer = new BitcoinStream(ms, true); serializer.ReadWrite(stakeModifierV2); serializer.ReadWrite(stakingCoins.Time); serializer.ReadWrite(prevout.Hash); serializer.ReadWrite(prevout.N); serializer.ReadWrite(transactionTime); context.Stake.HashProofOfStake = Hashes.Hash256(ms.ToArray()); } this.logger.LogTrace("Stake modifier V2 is '{0}', hash POS is '{1}'.", stakeModifierV2, context.Stake.HashProofOfStake); // Now check if proof-of-stake hash meets target protocol. BigInteger hashProofOfStakeTarget = new BigInteger(1, context.Stake.HashProofOfStake.ToBytes(false)); if (hashProofOfStakeTarget.CompareTo(weightedTarget) > 0) { this.logger.LogTrace("(-)[TARGET_MISSED]"); ConsensusErrors.StakeHashInvalidTarget.Throw(); } this.logger.LogTrace("(-)[OK]"); }
public virtual void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block) { Money blockReward = nFees + this.GetProofOfWorkReward(chainedBlock.Height); if (block.Transactions[0].TotalOut > blockReward) { ConsensusErrors.BadCoinbaseAmount.Throw(); } }
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 virtual void UpdateCoinView(ContextInformation context, Transaction tx) { this.logger.LogTrace("()"); ChainedBlock index = context.BlockValidationContext.ChainedBlock; UnspentOutputSet view = context.Set; view.Update(tx, index.Height); this.logger.LogTrace("(-)"); }
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 virtual void CheckBlockReward(ContextInformation context, Money nFees, ChainedBlock chainedBlock, Block block) { this.logger.LogTrace("()"); Money blockReward = nFees + this.GetProofOfWorkReward(chainedBlock.Height); if (block.Transactions[0].TotalOut > blockReward) { this.logger.LogTrace("(-)[BAD_COINBASE_AMOUNT]"); ConsensusErrors.BadCoinbaseAmount.Throw(); } this.logger.LogTrace("(-)"); }
public override void ExecuteBlock(ContextInformation context, TaskScheduler taskScheduler) { this.logger.LogTrace("()"); // Compute and store the stake proofs. this.CheckAndComputeStake(context); base.ExecuteBlock(context, taskScheduler); // TODO: A temporary fix till this methods is fixed in NStratis. (this.stakeChain as StakeChainStore).Set(context.BlockValidationContext.ChainedBlock, context.Stake.BlockStake); this.logger.LogTrace("(-)"); }
/// <summary> /// Validates a block using the consensus rules. /// </summary> public void ValidateBlock(ContextInformation context) { this.logger.LogTrace("()"); using (new StopwatchDisposable(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.BlockValidationContext.Block.Header.HashPrevBlock != this.Tip.HashBlock) { this.logger.LogTrace("Reorganization detected."); ConsensusErrors.InvalidPrevTip.Throw(); } this.logger.LogTrace("Validating new block."); // 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 memory leaks). context.BlockValidationContext.ChainedBlock = new ChainedBlock(context.BlockValidationContext.Block.Header, context.BlockValidationContext.Block.Header.GetHash(), this.Tip); // Liberate from memory the block created above if possible. context.BlockValidationContext.ChainedBlock = this.Chain.GetBlock(context.BlockValidationContext.ChainedBlock.HashBlock) ?? context.BlockValidationContext.ChainedBlock; context.SetBestBlock(this.dateTimeProvider.GetTimeOffset()); // == Validation flow == // Check the block header 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.BlockValidationContext.ChainedBlock); int lastCheckpointHeight = this.checkpoints.GetLastCheckpointHeight(); if (context.BlockValidationContext.ChainedBlock.Height > lastCheckpointHeight) { this.Validator.ContextualCheckBlock(context); // Check the block itself. this.Validator.CheckBlock(context); } else { this.logger.LogTrace("Block validation partially skipped because block height {0} is not greater than last checkpointed block height {1}.", context.BlockValidationContext.ChainedBlock.Height, lastCheckpointHeight); } } this.logger.LogTrace("(-)[OK]"); }
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 = this.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 = this.stakeChain.Get(pindexPrev.HashBlock); if (prevBlockStake == null) { ConsensusErrors.BadStakeBlock.Throw(); } //if (pBlockTime) pBlockTime = prevBlock.Header.Time; this.CheckStakeKernelHash(context, pindexPrev, nBits, prevBlock, prevUtxo, prevBlockStake, prevout, (uint)nTime); }
public virtual void ContextualCheckBlockHeader(ContextInformation context) { Guard.NotNull(context.BestBlock, nameof(context.BestBlock)); this.logger.LogTrace("()"); BlockHeader header = context.BlockValidationContext.Block.Header; int nHeight = context.BestBlock.Height + 1; // Check proof of work. if (header.Bits != context.NextWorkRequired) { this.logger.LogTrace("(-)[BAD_DIFF_BITS]"); ConsensusErrors.BadDiffBits.Throw(); } // Check timestamp against prev. if (header.BlockTime <= context.BestBlock.MedianTimePast) { this.logger.LogTrace("(-)[TIME_TOO_OLD]"); ConsensusErrors.TimeTooOld.Throw(); } // Check timestamp. if (header.BlockTime > (context.Time + TimeSpan.FromHours(2))) { this.logger.LogTrace("(-)[TIME_TOO_NEW]"); 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 >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP34])) || ((header.Version < 3) && (nHeight >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP66])) || ((header.Version < 4) && (nHeight >= this.consensusParams.BuriedDeployments[BuriedDeployments.BIP65]))) { this.logger.LogTrace("(-)[BAD_VERSION]"); ConsensusErrors.BadVersion.Throw(); } // Check that the block header hash matches the known checkpointed value, if any. if (!this.Checkpoints.CheckHardened(nHeight, header.GetHash())) { this.logger.LogTrace("(-)[CHECKPOINT_VIOLATION]"); ConsensusErrors.CheckpointViolation.Throw(); } this.logger.LogTrace("(-)[OK]"); }
/// <inheritdoc /> protected override void UpdateCoinView(ContextInformation context, Transaction transaction) { this.logger.LogTrace("()"); UnspentOutputSet view = context.Set; if (transaction.IsCoinStake) { context.Stake.TotalCoinStakeValueIn = view.GetValueIn(transaction); } base.UpdateCoinView(context, transaction); this.logger.LogTrace("(-)"); }
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 = this.coinView.FetchCoinsAsync(new[] { txIn.PrevOut.Hash }).GetAwaiter().GetResult(); if (coins == null || coins.UnspentOutputs.Length != 1) { ConsensusErrors.ReadTxPrevFailed.Throw(); } var prevBlock = this.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 override void ContextualCheckBlockHeader(ContextInformation context) { this.logger.LogTrace("()"); base.ContextualCheckBlockHeader(context); ChainedBlock chainedBlock = context.BlockValidationContext.ChainedBlock; this.logger.LogTrace("Height of block is {0}, block timestamp is {1}, previous block timestamp is {2}, block version is 0x{3:x}.", chainedBlock.Height, chainedBlock.Header.Time, chainedBlock.Previous.Header.Time, chainedBlock.Header.Version); if (chainedBlock.Header.Version < 7) { this.logger.LogTrace("(-)[BAD_VERSION]"); ConsensusErrors.BadVersion.Throw(); } if (context.Stake.BlockStake.IsProofOfWork() && (chainedBlock.Height > this.ConsensusParams.LastPOWBlock)) { this.logger.LogTrace("(-)[POW_TOO_HIGH]"); ConsensusErrors.ProofOfWorkTooHeigh.Throw(); } // Check coinbase timestamp. if (chainedBlock.Header.Time > this.FutureDrift(context.BlockValidationContext.Block.Transactions[0].Time)) { this.logger.LogTrace("(-)[TIME_TOO_NEW]"); ConsensusErrors.TimeTooNew.Throw(); } // Check coinstake timestamp. if (context.Stake.BlockStake.IsProofOfStake() && !this.CheckCoinStakeTimestamp(chainedBlock.Height, chainedBlock.Header.Time, context.BlockValidationContext.Block.Transactions[1].Time)) { this.logger.LogTrace("(-)[BAD_TIME]"); ConsensusErrors.StakeTimeViolation.Throw(); } // Check timestamp against prev. if (chainedBlock.Header.Time <= chainedBlock.Previous.Header.Time) { this.logger.LogTrace("(-)[TIME_TOO_EARLY]"); ConsensusErrors.BlockTimestampTooEarly.Throw(); } this.logger.LogTrace("(-)[OK]"); }
public void CheckKernel(ContextInformation context, ChainedBlock pindexPrev, uint nBits, long nTime, OutPoint prevout, ref long pBlockTime) { this.logger.LogTrace("({0}:'{1}',{2}:0x{3:X},{4}:{5},{6}:'{7}.{8}')", nameof(pindexPrev), pindexPrev, nameof(nBits), nBits, nameof(nTime), nTime, nameof(prevout), prevout.Hash, prevout.N); // TODO: https://github.com/stratisproject/StratisBitcoinFullNode/issues/397 FetchCoinsResponse coins = this.coinView.FetchCoinsAsync(new[] { prevout.Hash }).GetAwaiter().GetResult(); if ((coins == null) || (coins.UnspentOutputs.Length != 1)) { this.logger.LogTrace("(-)[READ_PREV_TX_FAILED]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } ChainedBlock prevBlock = this.chain.GetBlock(coins.BlockHash); if (prevBlock == null) { this.logger.LogTrace("(-)[REORG]"); ConsensusErrors.ReadTxPrevFailed.Throw(); } UnspentOutputs prevUtxo = coins.UnspentOutputs[0]; if (this.IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1)) { this.logger.LogTrace("(-)[LOW_COIN_AGE]"); ConsensusErrors.InvalidStakeDepth.Throw(); } BlockStake prevBlockStake = this.stakeChain.Get(pindexPrev.HashBlock); if (prevBlockStake == null) { this.logger.LogTrace("(-)[BAD_STAKE_BLOCK]"); ConsensusErrors.BadStakeBlock.Throw(); } pBlockTime = prevBlock.Header.Time; this.CheckStakeKernelHash(context, pindexPrev, nBits, prevBlock.Header.Time, prevBlockStake, prevUtxo, prevout, (uint)nTime); this.logger.LogTrace("(-):{0}={1}", nameof(pBlockTime), pBlockTime); }
public override void CheckBlockHeader(ContextInformation context) { this.logger.LogTrace("()"); context.SetStake(); if (context.Stake.BlockStake.IsProofOfWork()) { if (context.CheckPow && !context.BlockValidationContext.Block.Header.CheckProofOfWork()) { this.logger.LogTrace("(-)[HIGH_HASH]"); ConsensusErrors.HighHash.Throw(); } } context.NextWorkRequired = StakeValidator.GetNextTargetRequired(this.stakeChain, context.BlockValidationContext.ChainedBlock.Previous, context.Consensus, context.Stake.BlockStake.IsProofOfStake()); this.logger.LogTrace("(-)[OK]"); }
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); }
public void CheckProofOfStake(ContextInformation context, ChainedBlock pindexPrev, BlockStake prevBlockStake, Transaction tx, uint headerBits) { this.logger.LogTrace("({0}:'{1}',{2}.{3}:'{4}',{5}:0x{6:X})", nameof(pindexPrev), pindexPrev.HashBlock, nameof(prevBlockStake), nameof(prevBlockStake.HashProof), prevBlockStake.HashProof, nameof(headerBits), headerBits); if (!tx.IsCoinStake) { this.logger.LogTrace("(-)[NO_COINSTAKE]"); ConsensusErrors.NonCoinstake.Throw(); } // Kernel (input 0) must match the stake hash target per coin age (nBits). TxIn txIn = tx.Inputs[0]; // First try finding the previous transaction in database. FetchCoinsResponse coins = this.coinView.FetchCoinsAsync(new[] { txIn.PrevOut.Hash }).GetAwaiter().GetResult(); if ((coins == null) || (coins.UnspentOutputs.Length != 1)) { ConsensusErrors.ReadTxPrevFailed.Throw(); } ChainedBlock prevBlock = this.chain.GetBlock(coins.BlockHash); UnspentOutputs prevUtxo = coins.UnspentOutputs[0]; // Verify signature. if (!this.VerifySignature(prevUtxo, tx, 0, ScriptVerify.None)) { this.logger.LogTrace("(-)[BAD_SIGNATURE]"); ConsensusErrors.CoinstakeVerifySignatureFailed.Throw(); } // Min age requirement. if (this.IsConfirmedInNPrevBlocks(prevUtxo, pindexPrev, this.consensusOptions.StakeMinConfirmations - 1)) { this.logger.LogTrace("(-)[BAD_STAKE_DEPTH]"); ConsensusErrors.InvalidStakeDepth.Throw(); } this.CheckStakeKernelHash(context, pindexPrev, headerBits, prevBlock.Header.Time, prevBlockStake, prevUtxo, txIn.PrevOut, tx.Time); this.logger.LogTrace("(-)[OK]"); }
/// <summary> /// Validates a block using the consensus rules and executes it (processes it and adds it as a tip to consensus). /// </summary> /// <param name="context">A context that contains all information required to validate the block.</param> internal async Task ValidateAndExecuteBlockAsync(ContextInformation context) { this.logger.LogTrace("()"); this.ValidateBlock(context); // Load the UTXO set of the current block. UTXO may be loaded from cache or from disk. // The UTXO set is stored in the context. this.logger.LogTrace("Loading UTXO set of the new block."); context.Set = new UnspentOutputSet(); using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddUTXOFetchingTime(o))) { uint256[] ids = this.GetIdsToFetch(context.BlockValidationContext.Block, context.Flags.EnforceBIP30); FetchCoinsResponse coins = await this.UTXOSet.FetchCoinsAsync(ids).ConfigureAwait(false); context.Set.SetCoins(coins.UnspentOutputs); } // Attempt to load into the cache 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 is correctly spent. this.logger.LogTrace("Executing block."); using (new StopwatchDisposable(o => this.Validator.PerformanceCounter.AddBlockProcessingTime(o))) { this.Validator.ExecuteBlock(context, null); } // Persist the changes to the coinview. This will likely only be stored in memory, // unless the coinview treashold is reached. this.logger.LogTrace("Saving coinview changes."); await this.UTXOSet.SaveChangesAsync(context.Set.GetCoins(this.UTXOSet), null, this.Tip.HashBlock, context.BlockValidationContext.ChainedBlock.HashBlock).ConfigureAwait(false); // Set the new tip. this.Tip = context.BlockValidationContext.ChainedBlock; this.logger.LogTrace("(-)[OK]"); }