コード例 #1
0
        public async Task RunAsync_ProofOfStakeBlock_TransactionTimestampAfterBlockTimeStamp_ThrowsBlockTimeBeforeTrxConsensusErrorExceptionAsync()
        {
            var transaction = this.network.CreateTransaction();

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

            transaction = this.network.CreateTransaction();
            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            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[1].Time = (uint)1483747201;

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

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

            Assert.Equal(ConsensusErrors.BlockTimeBeforeTrx, exception.ConsensusError);
        }
コード例 #2
0
        /// <inheritdoc />
        protected override void CheckBlockReward(RuleContext 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("(-)");
        }
コード例 #3
0
        public void RunAsync_ProofOfStakeBlock_PayToPubKeyScriptPassesBlockSignatureValidation_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(new uint256(15), 1),
                ScriptSig = new Script()
            });

            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            var scriptPubKeyOut = new Script(Op.GetPushOp(this.key.PubKey.ToBytes(true)), OpcodeType.OP_CHECKSIG);

            transaction.Outputs.Add(new TxOut(Money.Zero, scriptPubKeyOut));
            block.Transactions.Add(transaction);

            ECDSASignature signature = this.key.Sign(block.GetHash());

            (block as PosBlock).BlockSignature = new BlockSignature {
                Signature = signature.ToDER()
            };

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

            this.consensusRules.RegisterRule <PosBlockSignatureRule>().Run(this.ruleContext);
        }
コード例 #4
0
        public void RunAsync_ProofOfStakeBlock_CoinStakePayToPubScriptKeyInvalid_ThrowsBadBlockSignatureConsensusErrorException()
        {
            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(new uint256(15), 1),
                ScriptSig = new Script()
            });

            // use different key with PayToPubKey script
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            var scriptPubKeyOut = new Script(Op.GetPushOp(new Key().PubKey.ToBytes(true)), OpcodeType.OP_CHECKSIG);

            transaction.Outputs.Add(new TxOut(Money.Zero, scriptPubKeyOut));
            block.Transactions.Add(transaction);

            ECDSASignature signature = this.key.Sign(block.GetHash());

            (block as PosBlock).BlockSignature = new BlockSignature {
                Signature = signature.ToDER()
            };

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

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

            Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
        }
コード例 #5
0
        public async Task RunAsync_ProofOfStakeBlock_OpCountBelowTwo_ThrowsBadBlockSignatureConsensusErrorExceptionAsync()
        {
            var block = new Block();

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

            var transaction = new Transaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });

            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            transaction.Outputs.Add(new TxOut(Money.Zero, new Script(new Op()
            {
                Code = OpcodeType.OP_RETURN
            })));
            block.Transactions.Add(transaction);

            ECDSASignature signature = this.key.Sign(block.GetHash());

            block.BlockSignatur = new BlockSignature {
                Signature = signature.ToDER()
            };

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

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

            Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
        }
コード例 #6
0
        public async Task RunAsync_ProofOfStakeBlock_FirstOpInScriptPubKeyNotOP_Return_ThrowsBadBlockSignatureConsensusErrorExceptionAsync()
        {
            var block = Network.StratisMain.Consensus.ConsensusFactory.CreateBlock();

            block.Transactions.Add(Network.StratisMain.Consensus.ConsensusFactory.CreateTransaction());

            var transaction = Network.StratisMain.Consensus.ConsensusFactory.CreateTransaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });

            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            transaction.Outputs.Add(new TxOut(Money.Zero, new Script(new Op()
            {
                Code = OpcodeType.OP_CHECKSIG
            })));
            block.Transactions.Add(transaction);

            ECDSASignature signature = this.key.Sign(block.GetHash());

            (block as PosBlock).BlockSignature = new BlockSignature {
                Signature = signature.ToDER()
            };

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

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

            Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
        }
コード例 #7
0
        public async Task RunAsync_ProofOfStakeBlock_PayToPubKeyScriptPassesBlockSignatureValidation_DoesNotThrowExceptionAsync()
        {
            var block = new Block();

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

            var transaction = new Transaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });

            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            Script scriptPubKeyOut = new Script(Op.GetPushOp(this.key.PubKey.ToBytes(true)), OpcodeType.OP_CHECKSIG);

            transaction.Outputs.Add(new TxOut(Money.Zero, scriptPubKeyOut));
            block.Transactions.Add(transaction);

            ECDSASignature signature = this.key.Sign(block.GetHash());

            block.BlockSignatur = new BlockSignature {
                Signature = signature.ToDER()
            };

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

            await this.consensusRules.RegisterRule <PosBlockSignatureRule>().RunAsync(this.ruleContext);
        }
コード例 #8
0
        public async Task RunAsync_ProofOfStakeBlock_CoinStakePayToPubScriptKeyInvalid_ThrowsBadBlockSignatureConsensusErrorExceptionAsync()
        {
            var block = new Block();

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

            var transaction = new Transaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });

            // use different key with PayToPubKey script
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            Script scriptPubKeyOut = new Script(Op.GetPushOp(new Key().PubKey.ToBytes(true)), OpcodeType.OP_CHECKSIG);

            transaction.Outputs.Add(new TxOut(Money.Zero, scriptPubKeyOut));
            block.Transactions.Add(transaction);

            ECDSASignature signature = this.key.Sign(block.GetHash());

            block.BlockSignatur = new BlockSignature {
                Signature = signature.ToDER()
            };

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

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

            Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
        }
コード例 #9
0
        public override Task RunAsync(RuleContext context)
        {
            if (context.SkipValidation)
            {
                return(Task.CompletedTask);
            }

            // Check consistency of ChainedHeader height and the height written in the coinbase tx
            var newHeight = GetHeightOfBlockToValidateSafe(context);

            // Get the algorithm of the block we are looking at
            bool isProofOfStake = BlockStake.IsProofOfStake(context.ValidationContext.BlockToValidate);

            // Check if there is a rule active, and if so, check if the algorithm is allowed at this height
            if (this.posConsensusOptions.IsAlgorithmAllowed(isProofOfStake, newHeight))
            {
                // yes, rule passed
                return(Task.CompletedTask);
            }

            // no, this block is not acceptable
            this.Logger.LogTrace("(-)[BAD-POS-POW-RATCHET-SEQUENCE]");
            X1ConsensusErrors.BadPosPowRatchetSequence.Throw();

            return(Task.CompletedTask);
        }
コード例 #10
0
        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("(-)");
        }
コード例 #11
0
        public async Task RunAsync_ProofOfStakeBlock_ValidBlock_DoesNotThrowExceptionAsync()
        {
            var transaction = this.network.CreateTransaction();

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

            transaction = this.network.CreateTransaction();
            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            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)1483747200;
            this.ruleContext.ValidationContext.Block.Transactions[1].Time = (uint)1483747200;

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

            await this.consensusRules.RegisterRule <PosCoinstakeRule>().RunAsync(this.ruleContext);
        }
コード例 #12
0
        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();
                }
            }
        }
コード例 #13
0
        /// <inheritdoc />
        public override void CheckBlockReward(RuleContext context, Money fees, int height, Block block)
        {
            if (BlockStake.IsProofOfStake(block))
            {
                var   posRuleContext  = context as PosRuleContext;
                Money stakeReward     = block.Transactions[1].TotalOut - posRuleContext.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();
                }
            }
        }
コード例 #14
0
        public async Task RunAsync_ProofOfStakeBlock_ScriptKeyPassesBlockSignatureValidation_DoesNotThrowExceptionAsync()
        {
            var block = Network.StratisMain.Consensus.ConsensusFactory.CreateBlock();

            block.Transactions.Add(Network.StratisMain.Consensus.ConsensusFactory.CreateTransaction());

            var transaction = Network.StratisMain.Consensus.ConsensusFactory.CreateTransaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });

            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            // push op_return to note external dependancy in front of pay to pubkey script so it does not match pay to pubkey template.
            Script scriptPubKeyOut = new Script(OpcodeType.OP_RETURN, Op.GetPushOp(this.key.PubKey.ToBytes(true)), OpcodeType.OP_CHECKSIG);

            transaction.Outputs.Add(new TxOut(Money.Zero, scriptPubKeyOut));
            block.Transactions.Add(transaction);

            ECDSASignature signature = this.key.Sign(block.GetHash());

            (block as PosBlock).BlockSignature = new BlockSignature {
                Signature = signature.ToDER()
            };

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

            await this.consensusRules.RegisterRule <PosBlockSignatureRule>().RunAsync(this.ruleContext);
        }
コード例 #15
0
        public void RunAsync_ProofOfStakeBlock_NoOpsInScriptPubKey_ThrowsBadBlockSignatureConsensusErrorException()
        {
            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(new uint256(15), 1),
                ScriptSig = new Script()
            });

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

            ECDSASignature signature = this.key.Sign(block.GetHash());

            (block as PosBlock).BlockSignature = new BlockSignature {
                Signature = signature.ToDER()
            };

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

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

            Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
        }
コード例 #16
0
        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]");
        }
コード例 #17
0
        /// <summary>
        /// This helper creates a coin staking block containing a coin staking transaction built according to
        /// the parameters and with valid first input and output. The block signature is created correctly with the
        /// private key corresponding to the public key.
        /// </summary>
        /// <param name="useCompressedKey">Determines whether the second transaction output will include a compressed
        /// (versus uncompressed) public key.</param>
        /// <param name="includeSecondPush">Determines whether the second transaction output will include a small integer
        /// after the public key.</param>
        /// <param name="expectFailure">Determines whether we expect failure (versus success).</param>
        private void ProofOfStakeBlock_CoinStakeTestHelper(bool useCompressedKey, bool includeSecondPush, bool expectFailure)
        {
            Block block = KnownNetworks.StratisMain.Consensus.ConsensusFactory.CreateBlock();

            // Add a dummy coinbase transaction.
            block.Transactions.Add(KnownNetworks.StratisMain.CreateTransaction());

            // Build a coinstake transaction.
            Transaction coinStakeTransaction = KnownNetworks.StratisMain.CreateTransaction();

            coinStakeTransaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });

            // First output of coinstake transaction is a special marker.
            coinStakeTransaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));

            // Second (unspendable) output.
            // Depending on the test case use either a compressed public key or an uncompressed public key.
            var pubKey  = useCompressedKey ? this.key.PubKey.Compress() : this.key.PubKey.Decompress();
            var opCodes = new List <Op> {
                OpcodeType.OP_RETURN, Op.GetPushOp(pubKey.ToBytes(true))
            };

            // Depending on the test case add a second push of some small integer.
            if (includeSecondPush)
            {
                opCodes.Add(Op.GetPushOp(new byte[] { 123 }));
            }
            coinStakeTransaction.Outputs.Add(new TxOut(Money.Zero, new Script(opCodes)));

            // Add the coinstake transaction.
            block.Transactions.Add(coinStakeTransaction);

            // Add a signature to the block.
            ECDSASignature signature = this.key.Sign(block.GetHash());

            (block as PosBlock).BlockSignature = new BlockSignature {
                Signature = signature.ToDER()
            };

            // Execute the PosBlockSignatureRule.
            this.ruleContext.ValidationContext.BlockToValidate = block;
            Assert.True(BlockStake.IsProofOfStake(this.ruleContext.ValidationContext.BlockToValidate));

            if (expectFailure)
            {
                ConsensusErrorException exception = Assert.Throws <ConsensusErrorException>(() =>
                                                                                            this.consensusRules.RegisterRule <PosBlockSignatureRule>().Run(this.ruleContext));
                Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
                return;
            }

            this.consensusRules.RegisterRule <PosBlockSignatureRule>().Run(this.ruleContext);
        }
コード例 #18
0
        private void OnBlockMined(MineBlockContext context)
        {
            this.logger.LogInformation("Mined new {0} block: '{1}'.", BlockStake.IsProofOfStake(context.ChainedHeaderBlock.Block) ? "POS" : "POW", context.ChainedHeaderBlock.ChainedHeader);

            context.CurrentHeight++;

            context.Blocks.Add(context.BlockTemplate.Block.GetHash());
            context.BlockTemplate = null;
        }
コード例 #19
0
        /// <inheritdoc />
        /// <exception cref="ConsensusErrors.BadStakeBlock">The coinbase output (first transaction) is not empty.</exception>
        /// <exception cref="ConsensusErrors.BadStakeBlock">The second transaction is not a coinstake transaction.</exception>
        /// <exception cref="ConsensusErrors.BadMultipleCoinstake">There are multiple coinstake tranasctions in the block.</exception>
        /// <exception cref="ConsensusErrors.BlockTimeBeforeTrx">The block contains a transaction with a timestamp after the block timestamp.</exception>
        public override Task RunAsync(RuleContext context)
        {
            if (context.SkipValidation)
            {
                return(Task.CompletedTask);
            }

            Block block = context.ValidationContext.BlockToValidate;

            // Check if the block was produced using POS.
            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))
                {
                    if (this.PosParent.Network.Consensus.PosEmptyCoinbase)
                    {
                        this.Logger.LogTrace("(-)[COINBASE_NOT_EMPTY]");
                        ConsensusErrors.BadStakeBlock.Throw();
                    }

                    // First output must be empty.
                    if ((!block.Transactions[0].Outputs[0].IsEmpty))
                    {
                        this.Logger.LogTrace("(-)[COINBASE_NOT_EMPTY]");
                        ConsensusErrors.BadStakeBlock.Throw();
                    }

                    // Check that the rest of the outputs are not spendable (op_return)
                    foreach (TxOut txOut in block.Transactions[0].Outputs.Skip(1))
                    {
                        // Only op_return are allowed in coinbase.
                        if (!txOut.ScriptPubKey.IsUnspendable)
                        {
                            this.Logger.LogTrace("(-)[COINBASE_SPENDABLE]");
                            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();
                }
            }

            return(Task.CompletedTask);
        }
コード例 #20
0
        /// <inheritdoc />
        public override void CheckBlockReward(RuleContext context, Money fees, int height, Block block)
        {
            // Currently this rule only applies to PoS blocks
            if (BlockStake.IsProofOfStake(block))
            {
                var         posRuleContext  = context as PosRuleContext;
                Transaction coinstake       = block.Transactions[1];
                Money       stakeReward     = coinstake.TotalOut - posRuleContext.TotalCoinStakeValueIn;
                Money       calcStakeReward = fees + this.GetProofOfStakeReward(height);

                this.Logger.LogDebug("Block stake reward is {0}, calculated reward is {1}.", stakeReward, calcStakeReward);
                if (stakeReward > calcStakeReward)
                {
                    this.Logger.LogTrace("(-)[BAD_COINSTAKE_AMOUNT]");
                    ConsensusErrors.BadCoinstakeAmount.Throw();
                }

                // Compute the total reward amount sent to the reward script.
                // We only mandate that at least x% of the reward is sent there, there are no other constraints on what gets done with the rest of the reward.
                Money rewardScriptTotal = Money.Coins(0.0m);

                foreach (TxOut output in coinstake.Outputs)
                {
                    // TODO: Double check which rule we have the negative output (and overflow) amount check inside; we assume that has been done before this check
                    if (output.ScriptPubKey == StraxCoinstakeRule.CirrusRewardScript)
                    {
                        rewardScriptTotal += output.Value;
                    }
                }

                // It must be CirrusRewardPercentage of the maximum possible reward precisely.
                // This additionally protects cold staking transactions from over-allocating to the Cirrus reward script at the expense of the non-Cirrus reward.
                // This means that the hot key can be used for staking by anybody and they will not be able to redirect the non-Cirrus reward to the Cirrus script.
                // It must additionally not be possible to short-change the Cirrus reward script by deliberately sacrificing part of the overall claimed reward.
                // TODO: Create a distinct consensus error for this?
                if ((calcStakeReward * CirrusRewardPercentage / 100) != rewardScriptTotal)
                {
                    this.Logger.LogTrace("(-)[BAD_COINSTAKE_REWARD_SCRIPT_AMOUNT]");
                    ConsensusErrors.BadCirrusRewardAmount.Throw();
                }

                // TODO: Perhaps we should limit it to a single output to prevent unnecessary UTXO set bloating
            }
            else
            {
                Money blockReward = fees + this.GetProofOfWorkReward(height);
                this.Logger.LogDebug("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();
                }

                // TODO: Should the reward split apply to blocks in the POW phase of the network too?
            }
        }
コード例 #21
0
        /// <inheritdoc/>
        public ChainedHeader GetLastPowPosChainedBlock(IStakeChain stakeChain, ChainedHeader startChainedHeader, bool proofOfStake)
        {
            Guard.Assert(startChainedHeader != null);

            BlockStake blockStake = stakeChain.Get(startChainedHeader.HashBlock);

            while ((startChainedHeader.Previous != null) && (blockStake.IsProofOfStake() != proofOfStake))
            {
                startChainedHeader = startChainedHeader.Previous;
                blockStake         = stakeChain.Get(startChainedHeader.HashBlock);
            }

            return(startChainedHeader);
        }
コード例 #22
0
        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();
                }
            }
        }
コード例 #23
0
ファイル: BlockProvider.cs プロジェクト: sonofsatoshi2020/xds
        /// <inheritdoc />
        public void BlockModified(ChainedHeader chainTip, Block block)
        {
            if (this.network.Consensus.IsProofOfStake)
            {
                if (BlockStake.IsProofOfStake(block))
                {
                    this.posBlockDefinition.BlockModified(chainTip, block);
                }
                else
                {
                    this.posPowBlockDefinition.BlockModified(chainTip, block);
                }
            }

            this.powBlockDefinition.BlockModified(chainTip, block);
        }
コード例 #24
0
        /// <inheritdoc />
        /// <exception cref="ConsensusErrors.BadStakeBlock">The coinbase output (first transaction) is not empty.</exception>
        /// <exception cref="ConsensusErrors.BadStakeBlock">The second transaction is not a coinstake transaction.</exception>
        /// <exception cref="ConsensusErrors.BadMultipleCoinstake">There are multiple coinstake tranasctions in the block.</exception>
        /// <exception cref="ConsensusErrors.BlockTimeBeforeTrx">The block contains a transaction with a timestamp after the block timestamp.</exception>
        public override Task RunAsync(RuleContext context)
        {
            if (context.SkipValidation)
            {
                return(Task.CompletedTask);
            }

            Block block = context.ValidationContext.BlockToValidate;

            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 transactions.
            foreach (Transaction transaction in block.Transactions)
            {
                // Check transaction timestamp.
                if (block.Header.Time < transaction.Time)
                {
                    this.Logger.LogDebug("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();
                }
            }

            return(Task.CompletedTask);
        }
コード例 #25
0
        /// <inheritdoc/>
        public ChainedHeader GetLastPowPosChainedBlock(IStakeChain stakeChain, ChainedHeader startChainedHeader, bool proofOfStake)
        {
            Guard.Assert(startChainedHeader != null);

            this.logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(startChainedHeader), startChainedHeader, nameof(proofOfStake), proofOfStake);

            BlockStake blockStake = stakeChain.Get(startChainedHeader.HashBlock);

            while ((startChainedHeader.Previous != null) && (blockStake.IsProofOfStake() != proofOfStake))
            {
                startChainedHeader = startChainedHeader.Previous;
                blockStake         = stakeChain.Get(startChainedHeader.HashBlock);
            }

            this.logger.LogTrace("(-)':{0}'", startChainedHeader);
            return(startChainedHeader);
        }
コード例 #26
0
        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);
        }
コード例 #27
0
        public static ChainedBlock GetLastBlockIndex(StakeChain stakeChain, ChainedBlock index, bool proofOfStake)
        {
            if (index == null)
            {
                throw new ArgumentNullException(nameof(index));
            }

            clogger.LogTrace("({0}:'{1}',{2}:{3})", nameof(index), index, nameof(proofOfStake), proofOfStake);

            BlockStake blockStake = stakeChain.Get(index.HashBlock);

            while ((index.Previous != null) && (blockStake.IsProofOfStake() != proofOfStake))
            {
                index      = index.Previous;
                blockStake = stakeChain.Get(index.HashBlock);
            }

            clogger.LogTrace("(-)':{0}'", index);
            return(index);
        }
コード例 #28
0
        public async Task RunAsync_ProofOfStakeBlock_CoinBaseNotEmpty_NoOutputsOnTransaction_ThrowsBadStakeBlockConsensusErrorExceptionAsync()
        {
            this.ruleContext.ValidationContext.BlockToValidate.Transactions.Add(new Transaction());

            var transaction = this.network.CreateTransaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            this.ruleContext.ValidationContext.BlockToValidate.Transactions.Add(transaction);

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

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

            Assert.Equal(ConsensusErrors.BadStakeBlock, exception.ConsensusError);
        }
コード例 #29
0
        public void RunAsync_ProofOfStakeBlockSignatureEmpty_ThrowsBadBlockSignatureConsensusErrorException()
        {
            this.ruleContext.ValidationContext.BlockToValidate = KnownNetworks.StratisMain.Consensus.ConsensusFactory.CreateBlock();
            this.ruleContext.ValidationContext.BlockToValidate.Transactions.Add(KnownNetworks.StratisMain.CreateTransaction());

            Transaction transaction = KnownNetworks.StratisMain.CreateTransaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            this.ruleContext.ValidationContext.BlockToValidate.Transactions.Add(transaction);

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

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

            Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
        }
コード例 #30
0
        public async Task RunAsync_ProofOfStakeBlockSignatureEmpty_ThrowsBadBlockSignatureConsensusErrorExceptionAsync()
        {
            this.ruleContext.BlockValidationContext.Block = new Block();
            this.ruleContext.BlockValidationContext.Block.Transactions.Add(new Transaction());

            var transaction = new Transaction();

            transaction.Inputs.Add(new TxIn()
            {
                PrevOut   = new OutPoint(new uint256(15), 1),
                ScriptSig = new Script()
            });
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
            this.ruleContext.BlockValidationContext.Block.Transactions.Add(transaction);

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

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

            Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError);
        }