public void RunRule_ProvenHeadersActive_And_InvalidMerkleProof_BadMerkleProofErrorIsThrown() { // Setup previous chained header. PosBlock prevPosBlock = new PosBlockBuilder(this.network).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).Build(); ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build(prevProvenBlockHeader); provenBlockHeader.PosBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash(); if (provenBlockHeader.Coinstake is IPosTransactionWithTime posTrx) { posTrx.Time = provenBlockHeader.Time; } // Corrupt merkle proof. provenBlockHeader.SetPrivateVariableValue("merkleProof", new PartialMerkleTree(new[] { new uint256(1234) }, new[] { false })); // 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); // Ensure that coinview returns a UTXO with valid outputs. var utxoOne = new UnspentOutput(prevPosBlock.Transactions[1].Inputs[0].PrevOut, new Coins((uint)previousChainedHeader.Height, new TxOut(), false, true)); // Setup coinstake transaction with a valid stake age. var res = new FetchCoinsResponse(); res.UnspentOutputs.Add(utxoOne.OutPoint, utxoOne); 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 hit bad merkle proof error. Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext); ruleValidation.Should().Throw <ConsensusErrorException>() .And.ConsensusError .Should().Be(ConsensusErrors.BadMerkleRoot); }
private bool WasUtxoSelectedForStaking(Network network, int chainTipHeight, int utxoHeight) { this.network = network; this.network.Consensus.Options = new PosConsensusOptions(); this.chain = GenerateChainWithBlockTimeAndHeight(2, this.network, 60, 0x1df88f6f); PosMinting miner = this.InitializePosMinting(); ChainedHeader chainTip = this.chain.Tip; chainTip.SetPrivatePropertyValue("Height", chainTipHeight); chainTip.Previous.SetPrivatePropertyValue("Height", utxoHeight); var descriptions = new List <UtxoStakeDescription>(); var utxoDescription = new UtxoStakeDescription(); utxoDescription.TxOut = new TxOut(new Money(100), new Mock <IDestination>().Object); utxoDescription.OutPoint = new OutPoint(uint256.One, 0); utxoDescription.HashBlock = chainTip.Previous.HashBlock; utxoDescription.UtxoSet = new UnspentOutputs(); utxoDescription.UtxoSet.SetPrivatePropertyValue("Time", chainTip.Header.Time); descriptions.Add(utxoDescription); List <UtxoStakeDescription> suitableCoins = miner.GetUtxoStakeDescriptionsSuitableForStakingAsync(descriptions, chainTip, chainTip.Header.Time + 64, long.MaxValue).GetAwaiter().GetResult(); return(suitableCoins.Count == 1); }
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_NullPreviousStake_InvalidPreviousProvenHeaderStakeModifierErrorIsThrown() { // Setup previous chained header. PosBlock prevPosBlock = new PosBlockBuilder(this.network).Build(); ProvenBlockHeader prevProvenBlockHeader = new ProvenBlockHeaderBuilder(prevPosBlock, this.network).Build(); prevProvenBlockHeader.StakeModifierV2 = null; // Forcing previous stake modifier to null. 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).Build(); ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build(); provenBlockHeader.PosBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash(); if (provenBlockHeader.Coinstake is IPosTransactionWithTime posTrx) { posTrx.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.provenHeadersActivationHeight + 2); // Ensure that coinview returns a UTXO with valid outputs. var utxoOneTransaction = this.network.CreateTransaction(); utxoOneTransaction.AddOutput(new TxOut()); var utxoOne = new UnspentOutput(new OutPoint(utxoOneTransaction, 0), new Coins((uint)this.provenHeadersActivationHeight + 10, utxoOneTransaction.Outputs.First(), false)); // Setup coinstake transaction with a valid stake age. var res = new FetchCoinsResponse(); res.UnspentOutputs.Add(utxoOne.OutPoint, utxoOne); 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); // When we run the validation rule, we should hit previous stake null error. Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext); ruleValidation.Should().Throw <ConsensusErrorException>() .And.ConsensusError .Should().Be(ConsensusErrors.InvalidPreviousProvenHeaderStakeModifier); }
public void InvalidStakeKernelHash_CoinstakeVerifySignatureErrorIsThrown() { // Setup previous chained header. PosBlock prevPosBlock = new PosBlockBuilder(this.network).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).Build(); ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build(prevProvenBlockHeader); provenBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash(); // 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); // Ensure that coinview returns a UTXO with valid outputs. var utxoOneTransaction = this.network.CreateTransaction(); utxoOneTransaction.AddOutput(new TxOut()); var utxoOne = new UnspentOutput(new OutPoint(utxoOneTransaction, 0), new Coins((uint)this.provenHeadersActivationHeight + 10, utxoOneTransaction.Outputs.First(), false)); // Setup coinstake transaction with a valid stake age. var res = new FetchCoinsResponse(); res.UnspentOutputs.Add(utxoOne.OutPoint, utxoOne); 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 fail 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>())) .Throws(new ConsensusErrorException(ConsensusErrors.StakeHashInvalidTarget)); // When we run the validation rule, we should hit stake hash invalid target error. Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext); ruleValidation.Should().Throw <ConsensusErrorException>() .And.ConsensusError .Should().Be(ConsensusErrors.StakeHashInvalidTarget); }
public void RunRule_ProvenHeadersActive_And_InvalidStakeKernelHash_CoinstakeVerifySignatureErrorIsThrown() { // Setup previous chained header. PosBlock prevPosBlock = new PosBlockBuilder(this.network).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).Build(); 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. this.coinView .Setup(m => m.FetchCoinsAsync(It.IsAny <uint256[]>(), It.IsAny <CancellationToken>())) .ReturnsAsync(new FetchCoinsResponse(new[] { new UnspentOutputs((uint)this.options.ProvenHeadersActivationHeight + 10, new Transaction()) }, posBlock.GetHash())); // 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); // 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 fail 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>())) .Throws(new ConsensusErrorException(ConsensusErrors.StakeHashInvalidTarget)); // When we run the validation rule, we should hit stake hash invalid target error. Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext); ruleValidation.Should().Throw <ConsensusErrorException>() .And.ConsensusError .Should().Be(ConsensusErrors.StakeHashInvalidTarget); }
public void RunRule_ProvenHeadersActive_And_InvalidStakeDepth_StakeDepthErrorIsThrown() { // Setup previous chained header. PosBlock prevPosBlock = new PosBlockBuilder(this.network).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).Build(); ProvenBlockHeader provenBlockHeader = new ProvenBlockHeaderBuilder(posBlock, this.network).Build(); provenBlockHeader.PosBlockHeader.HashPrevBlock = prevProvenBlockHeader.GetHash(); if (provenBlockHeader.Coinstake is IPosTransactionWithTime posTrx) { posTrx.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.provenHeadersActivationHeight + 2); // Ensure that coinview returns a UTXO with valid outputs. var utxoOne = new UnspentOutput(prevPosBlock.Transactions[1].Inputs[0].PrevOut, new Coins((uint)previousChainedHeader.Height, new TxOut(), false, true)); // Setup coinstake transaction with an invalid stake age. var res = new FetchCoinsResponse(); res.UnspentOutputs.Add(utxoOne.OutPoint, utxoOne); this.coinView .Setup(m => m.FetchCoins(It.IsAny <OutPoint[]>())) .Returns(res); // Setup stake validator to fail stake age check. this.stakeValidator .Setup(m => m.IsConfirmedInNPrevBlocks(It.IsAny <UnspentOutput>(), It.IsAny <ChainedHeader>(), It.IsAny <long>())) .Returns(true); // When we run the validation rule, we should hit coinstake depth error. Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext); ruleValidation.Should().Throw <ConsensusErrorException>() .And.ConsensusError .Should().Be(ConsensusErrors.InvalidStakeDepth); }
public void RunRule_ProvenHeadersActive_And_InvalidCoinstakeSignature_CoinstakeVerifySignatureErrorIsThrown() { // Setup previous chained header. PosBlock prevPosBlock = new PosBlockBuilder(this.network).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).Build(); 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.provenHeadersActivationHeight + 2); // Ensure that coinview returns UTXO with valid outputs. var utxoOneTransaction = new Transaction(); utxoOneTransaction.AddOutput(new TxOut()); var utxoOne = new UnspentOutputs((uint)this.provenHeadersActivationHeight + 10, utxoOneTransaction); // Setup coinstake transaction with a valid stake age. this.coinView .Setup(m => m.FetchCoinsAsync(It.IsAny <uint256[]>(), It.IsAny <CancellationToken>())) .ReturnsAsync(new FetchCoinsResponse(new[] { utxoOne }, posBlock.GetHash())); // Setup stake validator to fail signature validation. this.stakeValidator .Setup(m => m.VerifySignature(It.IsAny <UnspentOutputs>(), It.IsAny <Transaction>(), It.IsAny <int>(), It.IsAny <ScriptVerify>())) .Returns(false); // Setup stake validator to pass stake age check. this.stakeValidator .Setup(m => m.IsConfirmedInNPrevBlocks(It.IsAny <UnspentOutputs>(), It.IsAny <ChainedHeader>(), It.IsAny <long>())) .Returns(false); // When we run the validation rule, we should hit coinstake signature verification error. Action ruleValidation = () => this.consensusRules.RegisterRule <ProvenHeaderCoinstakeRule>().Run(this.ruleContext); ruleValidation.Should().Throw <ConsensusErrorException>().And.ConsensusError.Should().Be(ConsensusErrors.CoinstakeVerifySignatureFailed); }
/// <summary> /// Builds a chain of proven headers. /// </summary> /// <param name="blockCount">The amount of blocks to chain.</param> /// <param name="startingHeader">Build the chain from this header, if not start from genesis.</param> /// <returns>Tip of a created chain of headers.</returns> public ChainedHeader BuildProvenHeaderChain(int blockCount, ChainedHeader startingHeader = null) { startingHeader = startingHeader ?? ChainedHeadersHelper.CreateGenesisChainedHeader(this.Network); for (int i = 1; i < blockCount; i++) { PosBlock block = this.CreatePosBlock(); ProvenBlockHeader header = ((PosConsensusFactory)this.Network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block); header.Nonce = RandomUtils.GetUInt32(); header.HashPrevBlock = startingHeader.HashBlock; header.Bits = Target.Difficulty1; ChainedHeader prevHeader = startingHeader; startingHeader = new ChainedHeader(header, header.GetHash(), prevHeader.Height + 1); startingHeader.SetPrivatePropertyValue("Previous", prevHeader); prevHeader.Next.Add(startingHeader); } return(startingHeader); }
/// <returns>Tip of a created chain of headers.</returns> public ChainedHeader BuildChainWithProvenHeaders(int blockCount) { ChainedHeader currentHeader = ChainedHeadersHelper.CreateGenesisChainedHeader(this.Network); for (int i = 1; i < blockCount; i++) { PosBlock block = this.CreatePosBlockMock(); ProvenBlockHeader header = ((PosConsensusFactory)this.Network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block); header.Nonce = RandomUtils.GetUInt32(); header.HashPrevBlock = currentHeader.HashBlock; header.Bits = Target.Difficulty1; ChainedHeader prevHeader = currentHeader; currentHeader = new ChainedHeader(header, header.GetHash(), i); currentHeader.SetPrivatePropertyValue("Previous", prevHeader); prevHeader.Next.Add(currentHeader); } return(currentHeader); }
public (ChainedHeader chainedHeader, List <ProvenBlockHeader> provenBlockHeaders) BuildChainWithProvenHeaders(int blockCount, Network network, bool addNext = false) { Guard.Assert(blockCount > 0); var provenBlockHeaders = new List <ProvenBlockHeader>(); ChainedHeader chainedHeader = null; ChainedHeader previousChainHeader = null; for (int i = 0; i < blockCount; i++) { PosBlock block = CreatePosBlockMock(); ProvenBlockHeader header = ((PosConsensusFactory)this.Network.Consensus.ConsensusFactory).CreateProvenBlockHeader(block); header.Nonce = RandomUtils.GetUInt32(); header.HashPrevBlock = i > 0 ? chainedHeader.HashBlock : null; header.Bits = Target.Difficulty1; chainedHeader = new ChainedHeader(header, header.GetHash(), i); if (previousChainHeader != null) { chainedHeader.SetPrivatePropertyValue("Previous", previousChainHeader); if (addNext) { chainedHeader.Previous.Next.Add(chainedHeader); } } previousChainHeader = chainedHeader; provenBlockHeaders.Add(header); } return(chainedHeader, provenBlockHeaders); }
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); }