Example #1
0
        /// <summary>
        /// Checks if block signature is valid.
        /// TODO: Update this code to reflect changes made to the corresponding method in <see cref="Features.Consensus.Rules.CommonRules.PosBlockSignatureRule"/>.
        /// </summary>
        /// <param name="block">The block.</param>
        /// <returns><c>true</c> if the signature is valid, <c>false</c> otherwise.</returns>
        private bool CheckBlockSignature(SmartContractPosBlock block)
        {
            if (BlockStake.IsProofOfWork(block))
            {
                bool res = block.BlockSignature.IsEmpty();
                this.Logger.LogTrace("(-)[POW]:{0}", res);
                return(res);
            }

            if (block.BlockSignature.IsEmpty())
            {
                this.Logger.LogTrace("(-)[EMPTY]:false");
                return(false);
            }

            TxOut txout = block.Transactions[1].Outputs[1];

            if (PayToPubkeyTemplate.Instance.CheckScriptPubKey(txout.ScriptPubKey))
            {
                PubKey pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(txout.ScriptPubKey);
                bool   res    = pubKey.Verify(block.GetHash(), new ECDSASignature(block.BlockSignature.Signature));
                this.Logger.LogTrace("(-)[P2PK]:{0}", res);
                return(res);
            }

            // Block signing key also can be encoded in the nonspendable output.
            // This allows to not pollute UTXO set with useless outputs e.g. in case of multisig staking.

            List <Op> ops = txout.ScriptPubKey.ToOps().ToList();

            if (!ops.Any()) // script.GetOp(pc, opcode, vchPushValue))
            {
                this.Logger.LogTrace("(-)[NO_OPS]:false");
                return(false);
            }

            if (ops.ElementAt(0).Code != OpcodeType.OP_RETURN) // OP_RETURN)
            {
                this.Logger.LogTrace("(-)[NO_OP_RETURN]:false");
                return(false);
            }

            if (ops.Count < 2) // script.GetOp(pc, opcode, vchPushValue)
            {
                this.Logger.LogTrace("(-)[NO_SECOND_OP]:false");
                return(false);
            }

            byte[] data = ops.ElementAt(1).PushData;
            if (!ScriptEvaluationContext.IsCompressedOrUncompressedPubKey(data))
            {
                this.Logger.LogTrace("(-)[NO_PUSH_DATA]:false");
                return(false);
            }

            bool verifyRes = new PubKey(data).Verify(block.GetHash(), new ECDSASignature(block.BlockSignature.Signature));

            return(verifyRes);
        }
Example #2
0
        public static bool IsCanonicalBlockSignature(Block block, bool checkLowS)
        {
            if (BlockStake.IsProofOfWork(block))
            {
                return(block.BlockSignatur.IsEmpty());
            }

            return(checkLowS ?
                   ScriptEvaluationContext.IsLowDerSignature(block.BlockSignatur.Signature) :
                   ScriptEvaluationContext.IsValidSignatureEncoding(block.BlockSignatur.Signature));
        }
        public async Task RunAsync_ProofOfWorkBlock_ValidBlock_DoesNotThrowExceptionAsync()
        {
            var transaction = this.network.CreateTransaction();

            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            this.ruleContext.ValidationContext.BlockToValidate.Transactions.Add(transaction);
            this.ruleContext.ValidationContext.BlockToValidate.Header.Time = (uint)1483747200;

            Assert.True(BlockStake.IsProofOfWork(this.ruleContext.ValidationContext.BlockToValidate));

            await this.consensusRules.RegisterRule <PosCoinstakeRule>().RunAsync(this.ruleContext);
        }
        /// <summary>
        /// Checks if block signature is valid.
        /// </summary>
        /// <param name="block">The block.</param>
        /// <returns><c>true</c> if the signature is valid, <c>false</c> otherwise.</returns>
        private bool CheckBlockSignature(PosBlock block)
        {
            if (BlockStake.IsProofOfWork(block))
            {
                bool res = block.BlockSignature.IsEmpty();
                this.Logger.LogTrace("(-)[POW]:{0}", res);
                return(res);
            }

            var consensusRules = (PosConsensusRuleEngine)this.Parent;

            return(consensusRules.StakeValidator.CheckStakeSignature(block.BlockSignature, block.GetHash(), block.Transactions[1]));
        }
        public void ComputeStakeModifier(ChainBase chainIndex, ChainedBlock pindex, BlockStake blockStake)
        {
            var pindexPrev     = pindex.Previous;
            var blockStakePrev = pindexPrev == null ? null : this.stakeChain.Get(pindexPrev.HashBlock);

            // compute stake modifier
            var stakeContext = new StakeModifierContext();

            this.ComputeNextStakeModifier(chainIndex, pindexPrev, stakeContext);

            blockStake.SetStakeModifier(stakeContext.StakeModifier, stakeContext.GeneratedStakeModifier);
            blockStake.StakeModifierV2 = this.ComputeStakeModifierV2(
                pindexPrev, blockStakePrev, blockStake.IsProofOfWork() ? pindex.HashBlock : blockStake.PrevoutStake.Hash);
        }
        public void RunAsync_ProofOfWorkBlockSignatureNotEmpty_ThrowsBadBlockSignatureConsensusErrorException()
        {
            this.ruleContext.ValidationContext.BlockToValidate = KnownNetworks.StratisMain.Consensus.ConsensusFactory.CreateBlock();

            (this.ruleContext.ValidationContext.BlockToValidate as PosBlock).BlockSignature = new BlockSignature()
            {
                Signature = new byte[] { 0x2, 0x3 }
            };

            Assert.True(BlockStake.IsProofOfWork(this.ruleContext.ValidationContext.BlockToValidate));

            ConsensusErrorException exception = Assert.Throws <ConsensusErrorException>(() => this.consensusRules.RegisterRule <PosBlockSignatureRule>().Run(this.ruleContext));

            Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
        }
Example #7
0
        public async Task RunAsync_ProofOfWorkBlockSignatureNotEmpty_ThrowsBadBlockSignatureConsensusErrorExceptionAsync()
        {
            this.ruleContext.BlockValidationContext.Block = Network.StratisMain.Consensus.ConsensusFactory.CreateBlock();

            (this.ruleContext.BlockValidationContext.Block as PosBlock).BlockSignature = new BlockSignature()
            {
                Signature = new byte[] { 0x2, 0x3 }
            };

            Assert.True(BlockStake.IsProofOfWork(this.ruleContext.BlockValidationContext.Block));

            var exception = await Assert.ThrowsAsync <ConsensusErrorException>(() => this.consensusRules.RegisterRule <PosBlockSignatureRule>().RunAsync(this.ruleContext));

            Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
        }
        public static bool CheckBlockSignature(Block block)
        {
            if (BlockStake.IsProofOfWork(block))
            {
                return(block.BlockSignatur.IsEmpty());
            }

            if (block.BlockSignatur.IsEmpty())
            {
                return(false);
            }

            var txout = block.Transactions[1].Outputs[1];

            if (PayToPubkeyTemplate.Instance.CheckScriptPubKey(txout.ScriptPubKey))
            {
                var pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(txout.ScriptPubKey);
                return(pubKey.Verify(block.GetHash(), new ECDSASignature(block.BlockSignatur.Signature)));
            }

            if (StakeValidator.IsProtocolV3((int)block.Header.Time))
            {
                // Block signing key also can be encoded in the nonspendable output
                // This allows to not pollute UTXO set with useless outputs e.g. in case of multisig staking

                var ops = txout.ScriptPubKey.ToOps().ToList();
                if (!ops.Any())                 // script.GetOp(pc, opcode, vchPushValue))
                {
                    return(false);
                }
                if (ops.ElementAt(0).Code != OpcodeType.OP_RETURN)                 // OP_RETURN)
                {
                    return(false);
                }
                if (ops.Count < 2)                 // script.GetOp(pc, opcode, vchPushValue)
                {
                    return(false);
                }
                var data = ops.ElementAt(1).PushData;
                if (!ScriptEvaluationContext.IsCompressedOrUncompressedPubKey(data))
                {
                    return(false);
                }
                return(new PubKey(data).Verify(block.GetHash(), new ECDSASignature(block.BlockSignatur.Signature)));
            }

            return(false);
        }
Example #9
0
        public async Task RunAsync_ProofOfWorkBlock_TransactionTimestampAfterBlockTimeStamp_ThrowsBlockTimeBeforeTrxConsensusErrorExceptionAsync()
        {
            var transaction = this.network.CreateTransaction();

            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            this.ruleContext.ValidationContext.Block.Transactions.Add(transaction);

            this.ruleContext.ValidationContext.Block.Header.Time          = (uint)1483747200;
            this.ruleContext.ValidationContext.Block.Transactions[0].Time = (uint)1483747201;

            Assert.True(BlockStake.IsProofOfWork(this.ruleContext.ValidationContext.Block));

            ConsensusErrorException exception = await Assert.ThrowsAsync <ConsensusErrorException>(() => this.consensusRules.RegisterRule <PosCoinstakeRule>().RunAsync(this.ruleContext));

            Assert.Equal(ConsensusErrors.BlockTimeBeforeTrx, exception.ConsensusError);
        }
        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);
        }
Example #11
0
        public static bool IsCanonicalBlockSignature(PosBlock block, bool checkLowS)
        {
            if (BlockStake.IsProofOfWork(block))
            {
                return(block.BlockSignature.IsEmpty());
            }

            // A signature should have only one representation, or malleability vectors are introduced.
            // Therefore, an ECDSA signature, per BIP66, must be in strict DER encoding.
            // Additionally, the 'S' value of the signature must be the lower of the two possible values.

            // Recall that, for an ECDSA signature, the R and S values are both modulo N (aka the curve order).
            // Further, a signature (R, S) is equivalent to (R, -S mod N).

            // In other words there are always 2 valid S values, call them S and S'.

            // A small example of why S + S' = N:
            // N = 7
            // S = 4
            // ((N - S) % N) = 3 = S', therefore S + S' = 7 = N

            // Given S + S' = N, there will always be one S value greater than half the curve order
            // (N / 2), and one less than this.
            // The canonical signature is required to use the so-called 'low S' value, the one less than N / 2.

            // Therefore to get the other S' value (the complement) we calculate S' = N - S.

            // We can switch between the canonical and non-canonical form by calculating the complement of
            // whichever representation we currently have in a signature.

            // Using N + S will give a valid signature too, but will not give the complement, as (N + S) mod N = S.

            // For POS blocks that have a signature we do not append a SIGHASH type at the end of the signature.
            // Therefore IsValidSignatureEncoding should be called with haveSigHash = false when validating
            // POS blocks.

            return(checkLowS
                ? ScriptEvaluationContext.IsLowDerSignature(block.BlockSignature.Signature, false)
                : ScriptEvaluationContext.IsValidSignatureEncoding(block.BlockSignature.Signature, false));
        }
Example #12
0
        public async Task RunAsync_ProofOfWorkBlock_BlockSignatureEmpty_DoesNotThrowExceptionAsync()
        {
            var block = new Block();

            block.Transactions.Add(new Transaction());

            var transaction = new Transaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(uint256.Zero, uint.MaxValue),
                ScriptSig = new Script()
            });

            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            block.Transactions.Add(transaction);
            block.BlockSignatur = new BlockSignature();

            this.ruleContext.BlockValidationContext.Block = block;
            Assert.True(BlockStake.IsProofOfWork(this.ruleContext.BlockValidationContext.Block));

            await this.consensusRules.RegisterRule <PosBlockSignatureRule>().RunAsync(this.ruleContext);
        }
        public void RunAsync_ProofOfWorkBlock_BlockSignatureEmpty_DoesNotThrowException()
        {
            Block block = KnownNetworks.StratisMain.Consensus.ConsensusFactory.CreateBlock();

            block.Transactions.Add(KnownNetworks.StratisMain.CreateTransaction());

            Transaction transaction = KnownNetworks.StratisMain.CreateTransaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(uint256.Zero, uint.MaxValue),
                ScriptSig = new Script()
            });

            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            block.Transactions.Add(transaction);
            (block as PosBlock).BlockSignature = new BlockSignature();

            this.ruleContext.ValidationContext.BlockToValidate = block;
            Assert.True(BlockStake.IsProofOfWork(this.ruleContext.ValidationContext.BlockToValidate));

            this.consensusRules.RegisterRule <PosBlockSignatureRule>().Run(this.ruleContext);
        }
Example #14
0
        /// <summary>
        /// Checks and computes stake.
        /// </summary>
        /// <param name="context">Context that contains variety of information regarding blocks validation and execution.</param>
        /// <exception cref="ConsensusErrors.PrevStakeNull">Thrown if previous stake is not found.</exception>
        /// <exception cref="ConsensusErrors.SetStakeEntropyBitFailed">Thrown if failed to set stake entropy bit.</exception>
        private void CheckAndComputeStake(RuleContext context)
        {
            ChainedHeader chainedHeader = context.ValidationContext.ChainedHeaderToValidate;
            Block         block         = context.ValidationContext.BlockToValidate;

            var posRuleContext = context as PosRuleContext;

            if (posRuleContext.BlockStake == null)
            {
                posRuleContext.BlockStake = BlockStake.Load(context.ValidationContext.BlockToValidate);
            }

            BlockStake blockStake = posRuleContext.BlockStake;

            // Verify hash target and signature of coinstake tx.
            if (BlockStake.IsProofOfStake(block))
            {
                ChainedHeader prevChainedHeader = chainedHeader.Previous;

                BlockStake prevBlockStake = this.stakeChain.Get(prevChainedHeader.HashBlock);
                if (prevBlockStake == null)
                {
                    ConsensusErrors.PrevStakeNull.Throw();
                }

                // Only do proof of stake validation for blocks that are after the assumevalid block or after the last checkpoint.
                if (!context.SkipValidation)
                {
                    this.stakeValidator.CheckProofOfStake(posRuleContext, prevChainedHeader, prevBlockStake, block.Transactions[1], chainedHeader.Header.Bits.ToCompact());
                }
                else
                {
                    this.Logger.LogTrace("POS validation skipped for block at height {0}.", chainedHeader.Height);
                }
            }

            // PoW is checked in CheckBlock().
            if (BlockStake.IsProofOfWork(block))
            {
                posRuleContext.HashProofOfStake = chainedHeader.Header.GetPoWHash();
            }

            // Compute stake entropy bit for stake modifier.
            if (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit()))
            {
                this.Logger.LogTrace("(-)[STAKE_ENTROPY_BIT_FAIL]");
                ConsensusErrors.SetStakeEntropyBitFailed.Throw();
            }

            // Record proof hash value.
            blockStake.HashProof = posRuleContext.HashProofOfStake;

            int lastCheckpointHeight = this.Parent.Checkpoints.GetLastCheckpointHeight();

            if (chainedHeader.Height > lastCheckpointHeight)
            {
                // Compute stake modifier.
                ChainedHeader prevChainedHeader = chainedHeader.Previous;
                BlockStake    blockStakePrev    = prevChainedHeader == null ? null : this.stakeChain.Get(prevChainedHeader.HashBlock);
                blockStake.StakeModifierV2 = this.stakeValidator.ComputeStakeModifierV2(prevChainedHeader, blockStakePrev?.StakeModifierV2, blockStake.IsProofOfWork() ? chainedHeader.HashBlock : blockStake.PrevoutStake.Hash);
            }
            else if (chainedHeader.Height == lastCheckpointHeight)
            {
                // Copy checkpointed stake modifier.
                CheckpointInfo checkpoint = this.Parent.Checkpoints.GetCheckpoint(lastCheckpointHeight);
                blockStake.StakeModifierV2 = checkpoint.StakeModifierV2;
                this.Logger.LogTrace("Last checkpoint stake modifier V2 loaded: '{0}'.", blockStake.StakeModifierV2);
            }
            else
            {
                this.Logger.LogTrace("POS stake modifier computation skipped for block at height {0} because it is not above last checkpoint block height {1}.", chainedHeader.Height, lastCheckpointHeight);
            }
        }
        public void CheckAndComputeStake(ContextInformation context)
        {
            this.logger.LogTrace("()");

            ChainedBlock chainedBlock = context.BlockResult.ChainedBlock;
            Block        block        = context.BlockResult.Block;
            BlockStake   blockStake   = context.Stake.BlockStake;

            int lastCheckpointHeight = this.checkpoints.GetLastCheckpointHeight();

            // Verify hash target and signature of coinstake tx.
            if (BlockStake.IsProofOfStake(block))
            {
                ChainedBlock prevChainedBlock = chainedBlock.Previous;

                BlockStake prevBlockStake = this.stakeChain.Get(prevChainedBlock.HashBlock);
                if (prevBlockStake == null)
                {
                    ConsensusErrors.PrevStakeNull.Throw();
                }

                // Only do proof of stake validation for blocks after last checkpoint.
                if (chainedBlock.Height > lastCheckpointHeight)
                {
                    this.stakeValidator.CheckProofOfStake(context, prevChainedBlock, prevBlockStake, block.Transactions[1], chainedBlock.Header.Bits.ToCompact());
                }
                else
                {
                    this.logger.LogTrace("POS validation skipped for block at height {0} because it is not above last checkpoint block height {1}.", chainedBlock.Height, lastCheckpointHeight);
                }
            }

            // PoW is checked in CheckBlock().
            if (BlockStake.IsProofOfWork(block))
            {
                context.Stake.HashProofOfStake = chainedBlock.Header.GetPoWHash();
            }

            // Compute stake entropy bit for stake modifier.
            if (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit()))
            {
                this.logger.LogTrace("(-)[STAKE_ENTROPY_BIT_FAIL]");
                ConsensusErrors.SetStakeEntropyBitFailed.Throw();
            }

            // Record proof hash value.
            blockStake.HashProof = context.Stake.HashProofOfStake;

            if (chainedBlock.Height > lastCheckpointHeight)
            {
                // Compute stake modifier.
                ChainedBlock prevChainedBlock = chainedBlock.Previous;
                BlockStake   blockStakePrev   = prevChainedBlock == null ? null : this.stakeChain.Get(prevChainedBlock.HashBlock);
                blockStake.StakeModifierV2 = this.stakeValidator.ComputeStakeModifierV2(prevChainedBlock, blockStakePrev, blockStake.IsProofOfWork() ? chainedBlock.HashBlock : blockStake.PrevoutStake.Hash);
            }
            else if (chainedBlock.Height == lastCheckpointHeight)
            {
                // Copy checkpointed stake modifier.
                CheckpointInfo checkpoint = this.checkpoints.GetCheckpoint(lastCheckpointHeight);
                blockStake.StakeModifierV2 = checkpoint.StakeModifierV2;
                this.logger.LogTrace("Last checkpoint stake modifier V2 loaded: '{0}'.", blockStake.StakeModifierV2);
            }
            else
            {
                this.logger.LogTrace("POS stake modifier computation skipped for block at height {0} because it is not above last checkpoint block height {1}.", chainedBlock.Height, lastCheckpointHeight);
            }

            this.logger.LogTrace("(-)[OK]");
        }