public void RunRule_ProvenHeadersActive_And_InvalidCoinstakeKernelSignature_BadBlockSignatureErrorIsThrown() { // Setup private key. var mnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve); Key privateKey = mnemonic.DeriveExtKey().PrivateKey; // Setup previous chained header. PosBlock prevPosBlock = new PosBlockBuilder(this.network, privateKey).Build(); ProvenBlockHeader prevProvenBlockHeader = new ProvenBlockHeaderBuilder(prevPosBlock, this.network).Build(); var previousChainedHeader = new ChainedHeader(prevProvenBlockHeader, prevProvenBlockHeader.GetHash(), null); previousChainedHeader.SetPrivatePropertyValue("Height", this.options.ProvenHeadersActivationHeight + 1); // Setup proven header with valid coinstake. PosBlock posBlock = new PosBlockBuilder(this.network, privateKey).Build(); posBlock.UpdateMerkleRoot(); ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build(); provenBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash(); provenBlockHeader.Coinstake.Time = provenBlockHeader.Time; // Setup chained header and move it to the height higher than proven header activation height. this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(provenBlockHeader, provenBlockHeader.GetHash(), previousChainedHeader); this.ruleContext.ValidationContext.ChainedHeaderToValidate.SetPrivatePropertyValue("Height", this.options.ProvenHeadersActivationHeight + 2); // Setup coinstake transaction with a valid stake age. uint unspentOutputsHeight = (uint)this.options.ProvenHeadersActivationHeight + 10; var unspentOutputs = new UnspentOutputs(unspentOutputsHeight, new Transaction()) { Outputs = new[] { new TxOut(new Money(100), privateKey.PubKey) } }; this.coinView .Setup(m => m.FetchCoinsAsync(It.IsAny <uint256[]>(), It.IsAny <CancellationToken>())) .ReturnsAsync(new FetchCoinsResponse(new[] { unspentOutputs }, posBlock.GetHash())); // Setup stake validator to pass signature validation. this.stakeValidator .Setup(m => m.VerifySignature(It.IsAny <UnspentOutputs>(), It.IsAny <Transaction>(), It.IsAny <int>(), It.IsAny <ScriptVerify>())) .Returns(true); // Setup stake validator to pass stake kernel hash validation. this.stakeChain.Setup(m => m.Get(It.IsAny <uint256>())).Returns(new BlockStake()); this.stakeValidator .Setup(m => m.CheckStakeKernelHash(It.IsAny <PosRuleContext>(), It.IsAny <uint>(), It.IsAny <uint256>(), It.IsAny <UnspentOutputs>(), It.IsAny <OutPoint>(), It.IsAny <uint>())); // Setup stake validator to pass stake age check. this.stakeValidator .Setup(m => m.IsConfirmedInNPrevBlocks(It.IsAny <UnspentOutputs>(), It.IsAny <ChainedHeader>(), It.IsAny <long>())) .Returns(true); // When we run the validation rule, we should hit bad merkle proof error. Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext); ruleValidation.Should().Throw <ConsensusErrorException>() .And.ConsensusError .Should().Be(ConsensusErrors.BadBlockSignature); }
public void RunRule_ProvenHeadersActive_And_ValidProvenHeader_NoErrorsAreThrown() { // Setup private key. var mnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve); Key privateKey = mnemonic.DeriveExtKey().PrivateKey; // Setup previous chained header. PosBlock prevPosBlock = new PosBlockBuilder(this.network, privateKey).Build(); ProvenBlockHeader prevProvenBlockHeader = new ProvenBlockHeaderBuilder(prevPosBlock, this.network).Build(); var previousChainedHeader = new ChainedHeader(prevProvenBlockHeader, prevProvenBlockHeader.GetHash(), null); previousChainedHeader.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 1); // Setup proven header with valid coinstake. PosBlock posBlock = new PosBlockBuilder(this.network, privateKey).Build(); posBlock.UpdateMerkleRoot(); posBlock.Header.HashPrevBlock = prevProvenBlockHeader.GetHash(); posBlock.Header.Bits = 16777216; // Update signature. ECDSASignature signature = privateKey.Sign(posBlock.Header.GetHash()); posBlock.BlockSignature = new BlockSignature { Signature = signature.ToDER() }; ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build(prevProvenBlockHeader); provenBlockHeader.PosBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash(); if (provenBlockHeader.Coinstake is IPosTransactionWithTime posTrx) { posTrx.Time = provenBlockHeader.Time; } // Set invalid coinstake script pub key provenBlockHeader.Coinstake.Outputs[1].ScriptPubKey = privateKey.PubKey.ScriptPubKey; // Setup chained header and move it to the height higher than proven header activation height. this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(provenBlockHeader, provenBlockHeader.GetHash(), previousChainedHeader); this.ruleContext.ValidationContext.ChainedHeaderToValidate.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 2); // Setup coinstake transaction with a valid stake age. uint unspentOutputsHeight = (uint)this.provenHeadersActivationHeight + 10; var res = new FetchCoinsResponse(); var unspentOutputs = new UnspentOutput(prevPosBlock.Transactions[1].Inputs[0].PrevOut, new Coins(unspentOutputsHeight, new TxOut(new Money(100), privateKey.PubKey), false)); res.UnspentOutputs.Add(unspentOutputs.OutPoint, unspentOutputs); this.coinView .Setup(m => m.FetchCoins(It.IsAny <OutPoint[]>())) .Returns(res); // Setup stake validator to pass stake age check. this.stakeValidator .Setup(m => m.IsConfirmedInNPrevBlocks(It.IsAny <UnspentOutput>(), It.IsAny <ChainedHeader>(), It.IsAny <long>())) .Returns(false); // Setup stake validator to pass signature validation. this.stakeValidator .Setup(m => m.VerifySignature(It.IsAny <UnspentOutput>(), It.IsAny <Transaction>(), It.IsAny <int>(), It.IsAny <ScriptVerify>())) .Returns(true); // Setup stake validator to pass stake kernel hash validation. this.stakeChain.Setup(m => m.Get(It.IsAny <uint256>())).Returns(new BlockStake()); this.stakeValidator .Setup(m => m.CheckStakeKernelHash(It.IsAny <PosRuleContext>(), It.IsAny <uint>(), It.IsAny <uint256>(), It.IsAny <UnspentOutput>(), It.IsAny <OutPoint>(), It.IsAny <uint>())).Returns(true); // When we run the validation rule, we should not hit any errors. Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext); ruleValidation.Should().NotThrow(); }
public void RunRule_ProvenHeadersActive_And_InvalidCoinstakeKernelSignature_BadBlockSignatureErrorIsThrown() { // Setup private key. var mnemonic = new Mnemonic(Wordlist.English, WordCount.Twelve); Key privateKey = mnemonic.DeriveExtKey().PrivateKey; // Setup previous chained header. PosBlock prevPosBlock = new PosBlockBuilder(this.network, privateKey).Build(); ProvenBlockHeader prevProvenBlockHeader = new ProvenBlockHeaderBuilder(prevPosBlock, this.network).Build(); var previousChainedHeader = new ChainedHeader(prevProvenBlockHeader, prevProvenBlockHeader.GetHash(), null); previousChainedHeader.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 1); // Setup proven header with valid coinstake. PosBlock posBlock = new PosBlockBuilder(this.network, privateKey).Build(); posBlock.UpdateMerkleRoot(); ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build(prevProvenBlockHeader); provenBlockHeader.PosBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash(); if (provenBlockHeader.Coinstake is IPosTransactionWithTime posTrx) { posTrx.Time = provenBlockHeader.Time; } // Set invalid coinstake script pub key. provenBlockHeader.Coinstake.Outputs[1].ScriptPubKey = new Script("03cdac179a3391d96cf4957fa0255e4aa8055a993e92df7146e740117885b184ea OP_CHECKSIG"); // Setup chained header and move it to the height higher than proven header activation height. this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(provenBlockHeader, provenBlockHeader.GetHash(), previousChainedHeader); this.ruleContext.ValidationContext.ChainedHeaderToValidate.SetPrivatePropertyValue("Height", this.provenHeadersActivationHeight + 2); // Setup coinstake transaction with a valid stake age. uint unspentOutputsHeight = (uint)this.provenHeadersActivationHeight + 10; var res = new FetchCoinsResponse(); var unspentOutputs = new UnspentOutput(prevPosBlock.Transactions[1].Inputs[0].PrevOut, new Coins(unspentOutputsHeight, new TxOut(new Money(100), privateKey.PubKey), false)); res.UnspentOutputs.Add(unspentOutputs.OutPoint, unspentOutputs); this.coinView .Setup(m => m.FetchCoins(It.IsAny <OutPoint[]>())) .Returns(res); // Setup stake validator to pass signature validation. this.stakeValidator .Setup(m => m.VerifySignature(It.IsAny <UnspentOutput>(), It.IsAny <Transaction>(), It.IsAny <int>(), It.IsAny <ScriptVerify>())) .Returns(true); // Setup stake validator to pass stake kernel hash validation. this.stakeChain.Setup(m => m.Get(It.IsAny <uint256>())).Returns(new BlockStake()); this.stakeValidator .Setup(m => m.CheckStakeKernelHash(It.IsAny <PosRuleContext>(), It.IsAny <uint>(), It.IsAny <uint256>(), It.IsAny <UnspentOutput>(), It.IsAny <OutPoint>(), It.IsAny <uint>())).Returns(true); // Setup stake validator to pass stake age check. this.stakeValidator .Setup(m => m.IsConfirmedInNPrevBlocks(It.IsAny <UnspentOutput>(), It.IsAny <ChainedHeader>(), It.IsAny <long>())) .Returns(false); // When we run the validation rule, we should hit bad merkle proof error. Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext); ruleValidation.Should().Throw <ConsensusErrorException>() .And.ConsensusError .Should().Be(ConsensusErrors.BadBlockSignature); }