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();
                }
            }
        }
예제 #2
0
        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");
            //}
        }
예제 #4
0
        protected virtual void UpdateCoinView(ContextInformation context, Transaction tx)
        {
            ChainedBlock     index = context.BlockResult.ChainedBlock;
            UnspentOutputSet view  = context.Set;

            view.Update(tx, index.Height);
        }
예제 #5
0
        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();
            }
        }
예제 #6
0
        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);
        }
예제 #7
0
        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();
     }
 }
예제 #11
0
        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);
        }
예제 #17
0
        // 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;
        }
예제 #20
0
        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();
            }
        }
예제 #21
0
        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();
            }
        }
예제 #22
0
        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();
            }
        }