Commit ee6989c9 authored by Sergei Zubov's avatar Sergei Zubov

Merge coinview rule

parent 55c66483
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using NBitcoin;
using NBitcoin.Crypto;
using Stratis.Bitcoin.Base;
using Stratis.Bitcoin.Base.Deployments;
using Stratis.Bitcoin.BlockPulling;
using Stratis.Bitcoin.Configuration.Settings;
using Stratis.Bitcoin.Connection;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.Consensus.Validators;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Features.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.Rules.CommonRules;
using Stratis.Bitcoin.Interfaces;
using Stratis.Bitcoin.Tests.Common;
using Stratis.Bitcoin.Utilities;
using Xunit;
namespace Stratis.Bitcoin.Features.Consensus.Tests.Rules.CommonRules
{
/// <summary>
/// Test cases related to the <see cref="PosCoinviewRule"/>.
/// </summary>
public class DeStreamPosCoinViewRuleTests : TestPosConsensusRulesUnitTestBase
{
/// <summary>
/// Creates the consensus manager used by <see cref="PosCoinViewRuleFailsAsync"/>.
/// </summary>
/// <param name="unspentOutputs">The dictionary used to mock up the <see cref="ICoinView"/>.</param>
/// <returns>The constructed consensus manager.</returns>
private async Task<ConsensusManager> CreateConsensusManagerAsync(Dictionary<uint256, UnspentOutputs> unspentOutputs)
{
this.consensusSettings = new ConsensusSettings(Configuration.NodeSettings.Default(this.network));
var initialBlockDownloadState = new InitialBlockDownloadState(this.chainState.Object, this.network, this.consensusSettings, new Checkpoints());
// Register POS consensus rules.
new DeStreamFullNodeBuilderConsensusExtension.PosConsensusRulesRegistration().RegisterRules(this.network.Consensus);
ConsensusRuleEngine consensusRuleEngine = new PosConsensusRuleEngine(this.network, this.loggerFactory.Object, DateTimeProvider.Default,
this.concurrentChain, this.nodeDeployments, this.consensusSettings, this.checkpoints.Object, this.coinView.Object, this.stakeChain.Object,
this.stakeValidator.Object, this.chainState.Object, new InvalidBlockHashStore(this.dateTimeProvider.Object), new Mock<INodeStats>().Object, this.rewindDataIndexStore.Object)
.Register();
var headerValidator = new HeaderValidator(consensusRuleEngine, this.loggerFactory.Object);
var integrityValidator = new IntegrityValidator(consensusRuleEngine, this.loggerFactory.Object);
var partialValidator = new PartialValidator(consensusRuleEngine, this.loggerFactory.Object);
var fullValidator = new FullValidator(consensusRuleEngine, this.loggerFactory.Object);
// Create the chained header tree.
var chainedHeaderTree = new ChainedHeaderTree(this.network, this.loggerFactory.Object, headerValidator, this.checkpoints.Object,
this.chainState.Object, new Mock<IFinalizedBlockInfoRepository>().Object, this.consensusSettings, new InvalidBlockHashStore(new DateTimeProvider()));
// Create consensus manager.
var consensus = new ConsensusManager(chainedHeaderTree, this.network, this.loggerFactory.Object, this.chainState.Object, integrityValidator,
partialValidator, fullValidator, consensusRuleEngine, new Mock<IFinalizedBlockInfoRepository>().Object, new Signals.Signals(),
new Mock<IPeerBanning>().Object, initialBlockDownloadState, this.concurrentChain, new Mock<IBlockPuller>().Object, new Mock<IBlockStore>().Object,
new Mock<IConnectionManager>().Object, new Mock<INodeStats>().Object, new Mock<INodeLifetime>().Object);
// Mock the coinviews "FetchCoinsAsync" method. We will use the "unspentOutputs" dictionary to track spendable outputs.
this.coinView.Setup(d => d.FetchCoinsAsync(It.IsAny<uint256[]>(), It.IsAny<CancellationToken>()))
.Returns((uint256[] txIds, CancellationToken cancel) => Task.Run(() => {
var result = new UnspentOutputs[txIds.Length];
for (int i = 0; i < txIds.Length; i++)
result[i] = unspentOutputs.TryGetValue(txIds[i], out UnspentOutputs unspent) ? unspent : null;
return new FetchCoinsResponse(result, this.concurrentChain.Tip.HashBlock);
}));
// Mock the coinviews "GetTipHashAsync" method.
this.coinView.Setup(d => d.GetTipHashAsync(It.IsAny<CancellationToken>())).Returns(() => Task.Run(() => {
return this.concurrentChain.Tip.HashBlock;
}));
// Since we are mocking the stake validator ensure that GetNextTargetRequired returns something sensible. Otherwise we get the "bad-diffbits" error.
this.stakeValidator.Setup(s => s.GetNextTargetRequired(It.IsAny<IStakeChain>(), It.IsAny<ChainedHeader>(), It.IsAny<IConsensus>(), It.IsAny<bool>()))
.Returns(this.network.Consensus.PowLimit);
// Skip validation of signature in the proven header
this.stakeValidator.Setup(s => s.VerifySignature(It.IsAny<UnspentOutputs>(), It.IsAny<Transaction>(),0, It.IsAny<ScriptVerify>()))
.Returns(true);
// Since we are mocking the stakechain ensure that the Get returns a BlockStake. Otherwise this results in "previous stake is not found".
this.stakeChain.Setup(d => d.Get(It.IsAny<uint256>())).Returns(new BlockStake()
{
Flags = BlockFlag.BLOCK_PROOF_OF_STAKE,
StakeModifierV2 = 0,
StakeTime = (this.concurrentChain.Tip.Header.Time + 60) & ~PosConsensusOptions.StakeTimestampMask
});
// Since we are mocking the chainState ensure that the BlockStoreTip returns a usable value.
this.chainState.Setup(d => d.BlockStoreTip).Returns(this.concurrentChain.Tip);
// Since we are mocking the chainState ensure that the ConsensusTip returns a usable value.
this.chainState.Setup(d => d.ConsensusTip).Returns(this.concurrentChain.Tip);
// Initialize the consensus manager.
await consensus.InitializeAsync(this.concurrentChain.Tip);
return consensus;
}
/// <summary>
/// Tests whether an error is raised when a miner attempts to stake an output which he can't spend with his private key.
/// </summary>
/// <remarks>
/// <para>Create a "previous transaction" with 2 outputs. The first output is sent to miner 2 and the second output is sent to miner 1.
/// Now miner 2 creates a proof of stake block with coinstake transaction which will have two inputs corresponding to both the
/// outputs of the previous transaction. The coinstake transaction will be just two outputs, first is the coinstake marker and
/// the second is normal pay to public key that belongs to miner 2 with value that equals to the sum of the inputs.
/// The testable outcome is whether the consensus engine accepts such a block. Obviously, the test should fail if the block is accepted.
/// </para><para>
/// We use <see cref="ConsensusManager.BlockMinedAsync(Block)"/> to run partial validation and full validation rules and expect that
/// the rules engine will reject the block with the specific error of <see cref="ConsensusErrors.BadTransactionScriptError"/>,
/// which confirms that there was no valid signature on the second input - which corresponds to the output sent to miner 1.
/// </para></remarks>
[Fact]
public async Task PosCoinViewRuleFailsAsync()
{
var unspentOutputs = new Dictionary<uint256, UnspentOutputs>();
ConsensusManager consensusManager = await this.CreateConsensusManagerAsync(unspentOutputs);
// The keys used by miner 1 and miner 2.
var minerKey1 = new Key();
var minerKey2 = new Key();
// The scriptPubKeys (P2PK) of the miners.
Script scriptPubKey1 = minerKey1.PubKey.ScriptPubKey;
Script scriptPubKey2 = minerKey2.PubKey.ScriptPubKey;
// Create the block that we want to validate.
Block block = this.network.Consensus.ConsensusFactory.CreateBlock();
// Add dummy first transaction.
Transaction transaction = this.network.CreateTransaction();
transaction.Inputs.Add(TxIn.CreateCoinbase(this.concurrentChain.Tip.Height + 1));
transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
Assert.True(transaction.IsCoinBase);
// Add first transaction to block.
block.Transactions.Add(transaction);
// Create a previous transaction with scriptPubKey outputs.
Transaction prevTransaction = this.network.CreateTransaction();
// Coins sent to miner 2.
prevTransaction.Outputs.Add(new TxOut(Money.COIN * 5_000_000, scriptPubKey2));
// Coins sent to miner 1.
prevTransaction.Outputs.Add(new TxOut(Money.COIN * 10_000_000, scriptPubKey1));
// Record the spendable outputs.
unspentOutputs[prevTransaction.GetHash()] = new UnspentOutputs(1, prevTransaction);
// Create coin stake transaction.
Transaction coinstakeTransaction = this.network.CreateTransaction();
coinstakeTransaction.Inputs.Add(new TxIn(new OutPoint(prevTransaction, 0)));
coinstakeTransaction.Inputs.Add(new TxIn(new OutPoint(prevTransaction, 1)));
// Coinstake marker.
coinstakeTransaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null));
// Normal pay to public key that belongs to the second miner with value that
// equals to the sum of the inputs.
coinstakeTransaction.Outputs.Add(new TxOut(Money.COIN * 15_000_000, scriptPubKey2));
// The second miner signs the first transaction input which requires minerKey2.
// Miner 2 will not have minerKey1 so we leave the second ScriptSig empty/invalid.
new TransactionBuilder(this.network)
.AddKeys(minerKey2)
.AddCoins(new Coin(new OutPoint(prevTransaction, 0), prevTransaction.Outputs[0]))
.SignTransactionInPlace(coinstakeTransaction);
Assert.True(coinstakeTransaction.IsCoinStake);
// Add second transaction to block.
block.Transactions.Add(coinstakeTransaction);
// Finalize the block and add it to the chain.
block.Header.HashPrevBlock = this.concurrentChain.Tip.HashBlock;
block.Header.Time = (this.concurrentChain.Tip.Header.Time + 60) & ~PosConsensusOptions.StakeTimestampMask;
block.Header.Bits = block.Header.GetWorkRequired(this.network, this.concurrentChain.Tip);
block.SetPrivatePropertyValue("BlockSize", 1L);
block.Transactions[0].Time = block.Header.Time;
block.Transactions[1].Time = block.Header.Time;
block.UpdateMerkleRoot();
Assert.True(BlockStake.IsProofOfStake(block));
// Add a signature to the block.
ECDSASignature signature = minerKey2.Sign(block.GetHash());
(block as PosBlock).BlockSignature = new BlockSignature { Signature = signature.ToDER() };
// Execute the rule and check the outcome against what is expected.
ConsensusException error = await Assert.ThrowsAsync<ConsensusException>(async () => await consensusManager.BlockMinedAsync(block));
Assert.Equal(ConsensusErrors.BadTransactionScriptError.Message, error.Message);
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Moq;
using NBitcoin;
using Stratis.Bitcoin.Base;
using Stratis.Bitcoin.Base.Deployments;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Configuration.Settings;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Features.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.Rules.CommonRules;
using Stratis.Bitcoin.Tests.Common;
using Stratis.Bitcoin.Utilities;
using Xunit;
namespace Stratis.Bitcoin.Features.Consensus.Tests.Rules.CommonRules
{
/// <summary>
/// These tests only cover the first part of BIP68 and not the MaxSigOps, coinview update or scripts verify or calculate block rewards
/// </summary>
public class DeStreamPowCoinViewRuleTests
{
private Exception caughtExecption;
private readonly Network network;
private Mock<ILogger> logger;
private const int HeightOfBlockchain = 1;
private RuleContext ruleContext;
private UnspentOutputSet coinView;
private Transaction transactionWithCoinbaseFromPreviousBlock;
private readonly CoinViewRule rule;
public DeStreamPowCoinViewRuleTests()
{
this.network = KnownNetworks.RegTest;
this.rule = new DeStreamPowCoinviewRule();
}
[Fact]
public void RunAsync_ValidatingATransactionThatIsNotCoinBaseButStillHasUnspentOutputsWithoutInput_ThrowsBadTransactionMissingInput()
{
this.GivenACoinbaseTransactionFromAPreviousBlock();
this.AndARuleContext();
this.AndSomeUnspentOutputs();
this.AndATransactionWithNoUnspentOutputsAsInput();
this.WhenExecutingTheRule(this.rule, this.ruleContext);
this.ThenExceptionThrownIs(ConsensusErrors.BadTransactionMissingInput);
}
[Fact]
//NOTE: This is not full coverage of BIP68 bad transaction non final as a block earlier than allowable timestamp is also not allowable under BIP68.
public void RunAsync_ValidatingABlockHeightLowerThanBIP86Allows_ThrowsBadTransactionNonFinal()
{
this.GivenACoinbaseTransactionFromAPreviousBlock();
this.AndARuleContext();
this.AndSomeUnspentOutputs();
this.AndATransactionBlockHeightLowerThanBip68Allows();
this.WhenExecutingTheRule(this.rule, this.ruleContext);
this.ThenExceptionThrownIs(ConsensusErrors.BadTransactionNonFinal);
}
private void AndSomeUnspentOutputs()
{
this.coinView = new UnspentOutputSet();
this.coinView.SetCoins(new UnspentOutputs[0]);
(this.ruleContext as UtxoRuleContext).UnspentOutputSet = this.coinView;
this.coinView.Update(this.transactionWithCoinbaseFromPreviousBlock, 0);
}
private void AndARuleContext()
{
this.ruleContext = new PowRuleContext { };
this.ruleContext.ValidationContext = new ValidationContext();
BlockHeader blockHeader = this.network.Consensus.ConsensusFactory.CreateBlockHeader();
this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(blockHeader, new uint256("bcd7d5de8d3bcc7b15e7c8e5fe77c0227cdfa6c682ca13dcf4910616f10fdd06"), HeightOfBlockchain);
Block block = this.network.CreateBlock();
block.Transactions = new List<Transaction>();
this.ruleContext.ValidationContext.BlockToValidate = block;
}
protected void WhenExecutingTheRule(ConsensusRuleBase rule, RuleContext ruleContext)
{
try
{
this.logger = new Mock<ILogger>();
rule.Logger = this.logger.Object;
var dateTimeProvider = new DateTimeProvider();
rule.Parent = new PowConsensusRuleEngine(
KnownNetworks.RegTest,
new Mock<ILoggerFactory>().Object,
new Mock<IDateTimeProvider>().Object,
new ConcurrentChain(this.network),
new NodeDeployments(KnownNetworks.RegTest, new ConcurrentChain(this.network)),
new ConsensusSettings(NodeSettings.Default(KnownNetworks.RegTest)), new Mock<ICheckpoints>().Object, new Mock<ICoinView>().Object, new Mock<IChainState>().Object,
new InvalidBlockHashStore(dateTimeProvider),
new NodeStats(dateTimeProvider));
rule.Initialize();
(rule as AsyncConsensusRule).RunAsync(ruleContext).GetAwaiter().GetResult();
}
catch (Exception e)
{
this.caughtExecption = e;
}
}
private void ThenExceptionThrownIs(ConsensusError consensusErrorType)
{
this.caughtExecption.Should().NotBeNull();
this.caughtExecption.Should().BeOfType<ConsensusErrorException>();
var consensusErrorException = (ConsensusErrorException)this.caughtExecption;
consensusErrorException.ConsensusError.Should().Be(consensusErrorType);
}
private void AndATransactionBlockHeightLowerThanBip68Allows()
{
var transaction = new Transaction
{
Inputs = { new TxIn()
{
PrevOut = new OutPoint(this.transactionWithCoinbaseFromPreviousBlock, 0),
Sequence = HeightOfBlockchain + 1, //this sequence being higher triggers the ThrowsBadTransactionNonFinal
} },
Outputs = { new TxOut() },
Version = 2, // So that sequence locks considered (BIP68)
};
this.ruleContext.Flags = new DeploymentFlags() { LockTimeFlags = Transaction.LockTimeFlags.VerifySequence };
this.ruleContext.ValidationContext.BlockToValidate.Transactions.Add(transaction);
}
private void GivenACoinbaseTransactionFromAPreviousBlock()
{
this.transactionWithCoinbaseFromPreviousBlock = new Transaction();
var txIn = new TxIn { PrevOut = new OutPoint() };
this.transactionWithCoinbaseFromPreviousBlock.AddInput(txIn);
this.transactionWithCoinbaseFromPreviousBlock.AddOutput(new TxOut());
}
private void AndATransactionWithNoUnspentOutputsAsInput()
{
this.ruleContext.ValidationContext.BlockToValidate.Transactions.Add(new Transaction { Inputs = { new TxIn() } });
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Stratis.Bitcoin.BlockPulling;
using NBitcoin;
using NBitcoin.Rules;
using Stratis.Bitcoin.Base;
using Stratis.Bitcoin.Builder;
using Stratis.Bitcoin.Configuration.Logging;
using Stratis.Bitcoin.Configuration.Settings;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Features.Consensus.Interfaces;
using Stratis.Bitcoin.Features.Consensus.ProvenBlockHeaders;
using Stratis.Bitcoin.Features.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.Rules.CommonRules;
using Stratis.Bitcoin.Features.Consensus.Rules.ProvenHeaderRules;
using Stratis.Bitcoin.Interfaces;
namespace Stratis.Bitcoin.Features.Consensus
{
/// <summary>
/// A class providing extension methods for <see cref="IFullNodeBuilder" />.
/// A class providing extension methods for <see cref="IFullNodeBuilder"/>.
/// </summary>
public static class DeStreamFullNodeBuilderConsensusExtension
{
public static IFullNodeBuilder UseDeStreamPowConsensus(this IFullNodeBuilder fullNodeBuilder)
public static IFullNodeBuilder UsePowConsensus(this IFullNodeBuilder fullNodeBuilder)
{
LoggingConfiguration.RegisterFeatureNamespace<ConsensusFeature>("consensus");
LoggingConfiguration.RegisterFeatureClass<ConsensusStats>("bench");
LoggingConfiguration.RegisterFeatureNamespace<PowConsensusFeature>("powconsensus");
fullNodeBuilder.ConfigureFeature(features =>
{
features
.AddFeature<ConsensusFeature>()
.AddFeature<PowConsensusFeature>()
.FeatureServices(services =>
{
// TODO: this should be set on the network build
fullNodeBuilder.Network.Consensus.Options = new PowConsensusOptions();
services.AddSingleton<ICheckpoints, Checkpoints>();
services.AddSingleton<NBitcoin.Consensus.ConsensusOptions, PowConsensusOptions>();
services.AddSingleton<DBreezeCoinView, DeStreamDBreezeCoinView>();
services.AddSingleton<CoinView, CachedCoinView>();
services.AddSingleton<LookaheadBlockPuller>()
.AddSingleton<ILookaheadBlockPuller, LookaheadBlockPuller>(provider =>
provider.GetService<LookaheadBlockPuller>());
;
services.AddSingleton<IConsensusLoop, ConsensusLoop>()
.AddSingleton<INetworkDifficulty, ConsensusLoop>(provider =>
provider.GetService<IConsensusLoop>() as ConsensusLoop)
.AddSingleton<IGetUnspentTransaction, ConsensusLoop>(provider =>
provider.GetService<IConsensusLoop>() as ConsensusLoop);
services.AddSingleton<IInitialBlockDownloadState, InitialBlockDownloadState>();
services.AddSingleton<ConsensusOptions, ConsensusOptions>();
services.AddSingleton<DBreezeCoinView>();
services.AddSingleton<ICoinView, CachedCoinView>();
services.AddSingleton<ConsensusController>();
services.AddSingleton<ConsensusStats>();
services.AddSingleton<ConsensusSettings>();
services.AddSingleton<IConsensusRules, DeStreamPowConsensusRules>();
services
.AddSingleton<IRuleRegistration,
DeStreamPowConsensusRulesRegistration>();
services.AddSingleton<IConsensusRuleEngine, PowConsensusRuleEngine>();
services.AddSingleton<IChainState, ChainState>();
services.AddSingleton<ConsensusQuery>()
.AddSingleton<INetworkDifficulty, ConsensusQuery>(provider => provider.GetService<ConsensusQuery>())
.AddSingleton<IGetUnspentTransaction, ConsensusQuery>(provider => provider.GetService<ConsensusQuery>());
new PowConsensusRulesRegistration().RegisterRules(fullNodeBuilder.Network.Consensus);
});
});
return fullNodeBuilder;
}
public static IFullNodeBuilder UseDeStreamPosConsensus(this IFullNodeBuilder fullNodeBuilder)
public static IFullNodeBuilder UsePosConsensus(this IFullNodeBuilder fullNodeBuilder)
{
LoggingConfiguration.RegisterFeatureNamespace<ConsensusFeature>("consensus");
LoggingConfiguration.RegisterFeatureClass<ConsensusStats>("bench");
LoggingConfiguration.RegisterFeatureNamespace<PosConsensusFeature>("posconsensus");
fullNodeBuilder.ConfigureFeature(features =>
{
features
.AddFeature<ConsensusFeature>()
.AddFeature<PosConsensusFeature>()
.FeatureServices(services =>
{
fullNodeBuilder.Network.Consensus.Options = new DeStreamPosConsensusOptions();
services.AddSingleton<ICheckpoints, Checkpoints>();
services.AddSingleton<DBreezeCoinView, DeStreamDBreezeCoinView>();
services.AddSingleton<CoinView, CachedCoinView>();
services.AddSingleton<LookaheadBlockPuller>()
.AddSingleton<ILookaheadBlockPuller, LookaheadBlockPuller>(provider =>
provider.GetService<LookaheadBlockPuller>());
;
services.AddSingleton<IConsensusLoop, ConsensusLoop>()
.AddSingleton<INetworkDifficulty, ConsensusLoop>(provider =>
provider.GetService<IConsensusLoop>() as ConsensusLoop)
.AddSingleton<IGetUnspentTransaction, ConsensusLoop>(provider =>
provider.GetService<IConsensusLoop>() as ConsensusLoop);
services.AddSingleton<StakeChainStore>()
.AddSingleton<IStakeChain, StakeChainStore>(provider =>
provider.GetService<StakeChainStore>());
services.AddSingleton<DBreezeCoinView>();
services.AddSingleton<ICoinView, CachedCoinView>();
services.AddSingleton<StakeChainStore>().AddSingleton<IStakeChain, StakeChainStore>(provider => provider.GetService<StakeChainStore>());
services.AddSingleton<IStakeValidator, StakeValidator>();
services.AddSingleton<IInitialBlockDownloadState, InitialBlockDownloadState>();
services.AddSingleton<ConsensusController>();
services.AddSingleton<ConsensusStats>();
services.AddSingleton<ConsensusSettings>();
services.AddSingleton<IConsensusRules, DeStreamPosConsensusRules>();
services
.AddSingleton<IRuleRegistration,
DeStreamPosConsensusRulesRegistration>();
services.AddSingleton<IRewindDataIndexCache, RewindDataIndexCache>();
services.AddSingleton<IConsensusRuleEngine, PosConsensusRuleEngine>();
services.AddSingleton<IChainState, ChainState>();
services.AddSingleton<ConsensusQuery>()
.AddSingleton<INetworkDifficulty, ConsensusQuery>(provider => provider.GetService<ConsensusQuery>())
.AddSingleton<IGetUnspentTransaction, ConsensusQuery>(provider => provider.GetService<ConsensusQuery>());
services.AddSingleton<IProvenBlockHeaderStore, ProvenBlockHeaderStore>();
services.AddSingleton<IProvenBlockHeaderRepository, ProvenBlockHeaderRepository>();
new PosConsensusRulesRegistration().RegisterRules(fullNodeBuilder.Network.Consensus);
});
});
return fullNodeBuilder;
}
private class DeStreamPowConsensusRulesRegistration : IRuleRegistration
public class PowConsensusRulesRegistration : IRuleRegistration
{
public IEnumerable<ConsensusRule> GetRules()
public void RegisterRules(IConsensus consensus)
{
return new List<ConsensusRule>
consensus.HeaderValidationRules = new List<IHeaderValidationConsensusRule>()
{
new BlockHeaderRule(),
new HeaderTimeChecksRule(),
new CheckDifficultyPowRule(),
new BitcoinActivationRule(),
new BitcoinHeaderVersionRule()
};
// rules that are inside the method CheckBlockHeader
new CalculateWorkRule(),
consensus.IntegrityValidationRules = new List<IIntegrityValidationConsensusRule>()
{
new BlockMerkleRootRule()
};
// rules that are inside the method ContextualCheckBlockHeader
new CheckpointsRule(),
new AssumeValidRule(),
new BlockHeaderPowContextualRule(),
consensus.PartialValidationRules = new List<IPartialValidationConsensusRule>()
{
new SetActivationDeploymentsPartialValidationRule(),
// rules that are inside the method ContextualCheckBlock
new TransactionLocktimeActivationRule(), // implements BIP113
new CoinbaseHeightActivationRule(), // implements BIP34
new WitnessCommitmentsRule(), // BIP141, BIP144
new BlockSizeRule(),
// rules that are inside the method CheckBlock
new BlockMerkleRootRule(),
new EnsureCoinbaseRule(),
new CheckPowTransactionRule(),
new CheckSigOpsRule(),
};
consensus.FullValidationRules = new List<IFullValidationConsensusRule>()
{
new SetActivationDeploymentsFullValidationRule(),
// rules that require the store to be loaded (coinview)
new DeStreamLoadCoinviewRule(),
new DeStreamFundsPreservationRule(),
new LoadCoinviewRule(),
new TransactionDuplicationActivationRule(), // implements BIP30
new DeStreamPowCoinviewRule(), // implements BIP68, MaxSigOps and BlockReward calculation
new DeStreamBlockFeeRule()
new SaveCoinviewRule()
};
}
}
private class DeStreamPosConsensusRulesRegistration : IRuleRegistration
public class PosConsensusRulesRegistration : IRuleRegistration
{
public void RegisterRules(IConsensus consensus)
{
public IEnumerable<ConsensusRule> GetRules()
consensus.HeaderValidationRules = new List<IHeaderValidationConsensusRule>()
{
return new List<ConsensusRule>
new HeaderTimeChecksRule(),
new HeaderTimeChecksPosRule(),
new StratisBugFixPosFutureDriftRule(),
new CheckDifficultyPosRule(),
new StratisHeaderVersionRule(),
new ProvenHeaderSizeRule(),
new ProvenHeaderCoinstakeRule()
};
consensus.IntegrityValidationRules = new List<IIntegrityValidationConsensusRule>()
{
new BlockHeaderRule(),
new BlockMerkleRootRule(),
new PosBlockSignatureRepresentationRule(),
new PosBlockSignatureRule(),
};
// rules that are inside the method CheckBlockHeader
new CalculateStakeRule(),
consensus.PartialValidationRules = new List<IPartialValidationConsensusRule>()
{
new SetActivationDeploymentsPartialValidationRule(),
// rules that are inside the method ContextualCheckBlockHeader
new CheckpointsRule(),
new AssumeValidRule(),
new BlockHeaderPowContextualRule(),
new BlockHeaderPosContextualRule(),
new PosTimeMaskRule(),
// rules that are inside the method ContextualCheckBlock
new TransactionLocktimeActivationRule(), // implements BIP113
......@@ -165,24 +157,28 @@ namespace Stratis.Bitcoin.Features.Consensus
new WitnessCommitmentsRule(), // BIP141, BIP144
new BlockSizeRule(),
new PosBlockContextRule(), // TODO: this rule needs to be implemented
// rules that are inside the method CheckBlock
new BlockMerkleRootRule(),
new EnsureCoinbaseRule(),
new CheckPowTransactionRule(),
new CheckPosTransactionRule(),
new CheckSigOpsRule(),
new PosFutureDriftRule(),
new PosCoinstakeRule(),
new PosBlockSignatureRule(),
};
consensus.FullValidationRules = new List<IFullValidationConsensusRule>()
{
new SetActivationDeploymentsFullValidationRule(),
new CheckDifficultyHybridRule(),
// rules that require the store to be loaded (coinview)
new DeStreamLoadCoinviewRule(),
new DeStreamFundsPreservationRule(),
new LoadCoinviewRule(),
new TransactionDuplicationActivationRule(), // implements BIP30
new DeStreamPosCoinviewRule(), // implements BIP68, MaxSigOps and BlockReward calculation
new DeStreamBlockFeeRule()
// Place the PosColdStakingRule after the PosCoinviewRule to ensure that all input scripts have been evaluated
// and that the "IsColdCoinStake" flag would have been set by the OP_CHECKCOLDSTAKEVERIFY opcode if applicable.
new PosColdStakingRule(),
new SaveCoinviewRule()
};
}
}
......
......@@ -147,7 +147,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// </summary>
/// <param name="context">Context that contains variety of information regarding blocks validation and execution.</param>
/// <param name="transaction">Transaction which outputs will be added to the context's <see cref="UnspentOutputSet"/> and which inputs will be removed from it.</param>
protected void UpdateUTXOSet(RuleContext context, Transaction transaction)
protected virtual void UpdateUTXOSet(RuleContext context, Transaction transaction)
{
ChainedHeader index = context.ValidationContext.ChainedHeaderToValidate;
UnspentOutputSet view = (context as UtxoRuleContext).UnspentOutputSet;
......@@ -218,7 +218,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <exception cref="ConsensusErrors.BadTransactionInBelowOut">Thrown if transaction inputs are less then outputs.</exception>
/// <exception cref="ConsensusErrors.BadTransactionNegativeFee">Thrown if fees sum is negative.</exception>
/// <exception cref="ConsensusErrors.BadTransactionFeeOutOfRange">Thrown if fees value is out of range.</exception>
public void CheckInputs(Transaction transaction, UnspentOutputSet inputs, int spendHeight)
public virtual void CheckInputs(Transaction transaction, UnspentOutputSet inputs, int spendHeight)
{
if (!inputs.HaveInputs(transaction))
ConsensusErrors.BadTransactionMissingInput.Throw();
......@@ -280,7 +280,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <param name="inputs">Map of previous transactions that have outputs we're spending.</param>
/// <param name="flags">Script verification flags.</param>
/// <returns>Signature operation cost for all transaction's inputs.</returns>
public long GetTransactionSignatureOperationCost(Transaction transaction, UnspentOutputSet inputs, DeploymentFlags flags)
public virtual long GetTransactionSignatureOperationCost(Transaction transaction, UnspentOutputSet inputs, DeploymentFlags flags)
{
long signatureOperationCost = this.GetLegacySignatureOperationsCount(transaction) * this.ConsensusOptions.WitnessScaleFactor;
......@@ -308,7 +308,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <param name="witness">Witness script.</param>
/// <param name="flags">Script verification flags.</param>
/// <returns>Signature operation cost for single transaction input.</returns>
private long CountWitnessSignatureOperation(Script scriptPubKey, WitScript witness, DeploymentFlags flags)
protected long CountWitnessSignatureOperation(Script scriptPubKey, WitScript witness, DeploymentFlags flags)
{
witness = witness ?? WitScript.Empty;
if (!flags.ScriptFlags.HasFlag(ScriptVerify.Witness))
......@@ -337,7 +337,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <param name="transaction">Transaction for which we are computing the cost.</param>
/// <param name="inputs">Map of previous transactions that have outputs we're spending.</param>
/// <returns>Signature operation cost for transaction.</returns>
private uint GetP2SHSignatureOperationsCount(Transaction transaction, UnspentOutputSet inputs)
protected virtual uint GetP2SHSignatureOperationsCount(Transaction transaction, UnspentOutputSet inputs)
{
if (transaction.IsCoinBase)
return 0;
......@@ -358,7 +358,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// </summary>
/// <param name="transaction">Transaction for which we are computing the cost.</param>
/// <returns>Legacy signature operation cost for transaction.</returns>
private long GetLegacySignatureOperationsCount(Transaction transaction)
protected long GetLegacySignatureOperationsCount(Transaction transaction)
{
long sigOps = 0;
foreach (TxIn txin in transaction.Inputs)
......@@ -375,7 +375,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// </summary>
/// <param name="value">The value to be checked.</param>
/// <returns><c>true</c> if the value is in range. Otherwise <c>false</c>.</returns>
private bool MoneyRange(long value)
protected bool MoneyRange(long value)
{
return ((value >= 0) && (value <= this.Consensus.MaxMoney));
}
......
......@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Base.Deployments;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Utilities;
......@@ -17,91 +18,59 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
{
public override async Task RunAsync(RuleContext context)
{
this.Logger.LogTrace("()");
Block block = context.ValidationContext.Block;
ChainedHeader index = context.ValidationContext.ChainedHeader;
Block block = context.ValidationContext.BlockToValidate;
ChainedHeader index = context.ValidationContext.ChainedHeaderToValidate;
DeploymentFlags flags = context.Flags;
UnspentOutputSet view = (context as UtxoRuleContext).UnspentOutputSet;
this.Parent.PerformanceCounter.AddProcessedBlocks(1);
long sigOpsCost = 0;
Money fees = Money.Zero;
var checkInputs = new List<Task<bool>>();
for (int txIndex = 0; txIndex < block.Transactions.Count; txIndex++)
{
this.Parent.PerformanceCounter.AddProcessedTransactions(1);
Transaction tx = block.Transactions[txIndex];
if (!context.SkipValidation)
{
if (!this.IsProtocolTransaction(tx))
{
if (!view.HaveInputs(tx))
if (!tx.IsCoinBase && !view.HaveInputs(tx))
{
this.Logger.LogTrace("(-)[BAD_TX_NO_INPUT]");
ConsensusErrors.BadTransactionMissingInput.Throw();
}
var prevheights = new int[tx.Inputs.Count];
// Check that transaction is BIP68 final.
// BIP68 lock checks (as opposed to nLockTime checks) must
// be in ConnectBlock because they require the UTXO set.
for (int j = 0; j < tx.Inputs.Count; j++)
{
prevheights[j] = tx.Inputs[j].IsChangePointer()
? 0
: (int) view.AccessCoins(tx.Inputs[j].PrevOut.Hash).Height;
}
if (!tx.CheckSequenceLocks(prevheights, index, flags.LockTimeFlags))
if (!this.IsTxFinal(tx, context))
{
this.Logger.LogTrace("(-)[BAD_TX_NON_FINAL]");
ConsensusErrors.BadTransactionNonFinal.Throw();
}
}
// GetTransactionSignatureOperationCost counts 3 types of sigops:
// * legacy (always),
// * legacy (always),AccessCoins(tx.Inputs[j].PrevOut.Hash).Height
// * p2sh (when P2SH enabled in flags and excludes coinbase),
// * witness (when witness enabled in flags and excludes coinbase).
sigOpsCost += this.GetTransactionSignatureOperationCost(tx, view, flags);
if (sigOpsCost > this.PowConsensusOptions.MaxBlockSigopsCost)
if (sigOpsCost > this.ConsensusOptions.MaxBlockSigopsCost)
{
this.Logger.LogTrace("(-)[BAD_BLOCK_SIG_OPS]");
ConsensusErrors.BadBlockSigOps.Throw();
}
if (!this.IsProtocolTransaction(tx))
if (!tx.IsCoinBase)
{
this.CheckInputs(tx, view, index.Height);
if (!tx.IsCoinStake)
fees += view.GetValueIn(tx) - tx.TotalOut;
var txData = new PrecomputedTransactionData(tx);
for (int inputIndex = 0; inputIndex < tx.Inputs.Count; inputIndex++)
{
this.Parent.PerformanceCounter.AddProcessedInputs(1);
TxIn input = tx.Inputs[inputIndex];
int inputIndexCopy = inputIndex;
TxOut txout = input.IsChangePointer()
? tx.Outputs[input.PrevOut.N]
: view.GetOutputFor(input);
var checkInput = new Task<bool>(() =>
{
var checker = new TransactionChecker(tx, inputIndexCopy, txout.Value, txData);
var ctx = new ScriptEvaluationContext(this.Parent.Network);
ctx.ScriptVerify = flags.ScriptFlags;
bool verifyScriptResult =
ctx.VerifyScript(input.ScriptSig, txout.ScriptPubKey, checker);
if (verifyScriptResult == false)
{
this.Logger.LogTrace(
"Verify script for transaction '{0}' failed, ScriptSig = '{1}', ScriptPubKey = '{2}', script evaluation error = '{3}'",
tx.GetHash(), input.ScriptSig, txout.ScriptPubKey, ctx.Error);
}
return verifyScriptResult;
});
var checkInput = new Task<bool>(() => this.CheckInput(tx, inputIndexCopy, txout, txData, input, flags));
checkInput.Start();
checkInputs.Add(checkInput);
}
......@@ -136,14 +105,12 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <inheritdoc />
public override void CheckInputs(Transaction transaction, UnspentOutputSet inputs, int spendHeight)
{
this.Logger.LogTrace("({0}:{1})", nameof(spendHeight), spendHeight);
if (!inputs.HaveInputs(transaction))
ConsensusErrors.BadTransactionMissingInput.Throw();
Money valueIn = Money.Zero;
Money fees = Money.Zero;
foreach (TxIn txIn in transaction.Inputs.RemoveChangePointer())
foreach (var txIn in transaction.Inputs.RemoveChangePointer())
{
OutPoint prevout = txIn.PrevOut;
UnspentOutputs coins = inputs.AccessCoins(prevout.Hash);
......@@ -159,13 +126,15 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
}
}
if (!transaction.IsProtocolTransaction())
{
if (valueIn < transaction.TotalOut)
{
this.Logger.LogTrace("(-)[TX_IN_BELOW_OUT]");
ConsensusErrors.BadTransactionInBelowOut.Throw();
}
// Tally transaction fees.
// Check transaction fees.
Money txFee = valueIn - transaction.TotalOut;
if (txFee < 0)
{
......@@ -179,29 +148,28 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
this.Logger.LogTrace("(-)[BAD_FEE]");
ConsensusErrors.BadTransactionFeeOutOfRange.Throw();
}
this.Logger.LogTrace("(-)");
}
}
/// <inheritdoc />
public override long GetTransactionSignatureOperationCost(Transaction transaction, UnspentOutputSet inputs,
DeploymentFlags flags)
{
long signatureOperationCost = this.GetLegacySignatureOperationsCount(transaction) *
this.PowConsensusOptions.WitnessScaleFactor;
long signatureOperationCost = this.GetLegacySignatureOperationsCount(transaction) * this.ConsensusOptions.WitnessScaleFactor;
if (transaction.IsCoinBase)
return signatureOperationCost;
if (flags.ScriptFlags.HasFlag(ScriptVerify.P2SH))
{
signatureOperationCost += this.GetP2SHSignatureOperationsCount(transaction, inputs) *
this.PowConsensusOptions.WitnessScaleFactor;
signatureOperationCost += this.GetP2SHSignatureOperationsCount(transaction, inputs) * this.ConsensusOptions.WitnessScaleFactor;
}
signatureOperationCost += (from t in transaction.Inputs.RemoveChangePointer()
let prevout = inputs.GetOutputFor(t)
select this.CountWitnessSignatureOperation(prevout.ScriptPubKey, t.WitScript, flags)).Sum();
foreach (var txIn in transaction.Inputs.RemoveChangePointer())
{
TxOut prevout = inputs.GetOutputFor(txIn);
signatureOperationCost += this.CountWitnessSignatureOperation(prevout.ScriptPubKey, txIn.WitScript, flags);
}
return signatureOperationCost;
}
......@@ -213,7 +181,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
return 0;
uint sigOps = 0;
foreach (TxIn txIn in transaction.Inputs.RemoveChangePointer())
foreach (var txIn in transaction.Inputs.RemoveChangePointer())
{
TxOut prevout = inputs.GetOutputFor(txIn);
if (prevout.ScriptPubKey.IsPayToScriptHash(this.Parent.Network))
......@@ -228,34 +196,31 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
{
// Saves script pub keys and total amount of spent inputs to context
this.Logger.LogTrace("()");
ChainedHeader index = context.ValidationContext.ChainedHeader;
ChainedHeader index = context.ValidationContext.ChainedHeaderToValidate;
UnspentOutputSet view = (context as UtxoRuleContext).UnspentOutputSet;
switch (context)
{
case DeStreamPowRuleContext deStreamPowRuleContext:
deStreamPowRuleContext.InputScriptPubKeys.AddOrReplace(transaction.GetHash(), transaction.Inputs
.RemoveChangePointer()
.Select(p => view.GetOutputFor(p).ScriptPubKey).ToList());
deStreamPowRuleContext.TotalIn.Add(transaction.GetHash(), view.GetValueIn(transaction));
break;
case DeStreamRuleContext deStreamPosRuleContext:
deStreamPosRuleContext.InputScriptPubKeys.AddOrReplace(transaction.GetHash(), transaction.Inputs
.RemoveChangePointer()
.Select(p => view.GetOutputFor(p).ScriptPubKey).ToList());
deStreamPosRuleContext.TotalIn.Add(transaction.GetHash(), view.GetValueIn(transaction));
break;
default:
throw new NotSupportedException(
$"Rule context must be {nameof(DeStreamPowRuleContext)} or {nameof(DeStreamRuleContext)}");
}
view.Update(transaction, index.Height);
//TODO
// switch (context)
// {
// case DeStreamPowRuleContext deStreamPowRuleContext:
// deStreamPowRuleContext.InputScriptPubKeys.AddOrReplace(transaction.GetHash(), transaction.Inputs
// .RemoveChangePointer()
// .Select(p => view.GetOutputFor(p).ScriptPubKey).ToList());
// deStreamPowRuleContext.TotalIn.Add(transaction.GetHash(), view.GetValueIn(transaction));
// break;
//
// case DeStreamRuleContext deStreamPosRuleContext:
// deStreamPosRuleContext.InputScriptPubKeys.AddOrReplace(transaction.GetHash(), transaction.Inputs
// .RemoveChangePointer()
// .Select(p => view.GetOutputFor(p).ScriptPubKey).ToList());
// deStreamPosRuleContext.TotalIn.Add(transaction.GetHash(), view.GetValueIn(transaction));
// break;
// default:
// throw new NotSupportedException(
// $"Rule context must be {nameof(DeStreamPowRuleContext)} or {nameof(DeStreamRuleContext)}");
// }
this.Logger.LogTrace("(-)");
view.Update(transaction, index.Height);
}
}
}
\ No newline at end of file
using System.Threading.Tasks;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Features.Consensus.Interfaces;
using Stratis.Bitcoin.Utilities;
......@@ -10,7 +12,6 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <summary>
/// Proof of stake override for the coinview rules - BIP68, MaxSigOps and BlockReward checks.
/// </summary>
[ExecutionRule]
public sealed class DeStreamPosCoinviewRule : DeStreamCoinViewRule
{
/// <summary>Provides functionality for checking validity of PoS blocks.</summary>
......@@ -20,37 +21,29 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
private IStakeChain stakeChain;
/// <summary>The consensus of the parent Network.</summary>
private NBitcoin.Consensus consensus;
private IConsensus consensus;
/// <inheritdoc />
public override void Initialize()
{
this.Logger.LogTrace("()");
base.Initialize();
this.consensus = this.Parent.Network.Consensus;
var consensusRules = (PosConsensusRules)this.Parent;
var consensusRules = (PosConsensusRuleEngine)this.Parent;
this.stakeValidator = consensusRules.StakeValidator;
this.stakeChain = consensusRules.StakeChain;
this.Logger.LogTrace("(-)");
}
/// <inheritdoc />
/// <summary>Compute and store the stake proofs.</summary>
public override async Task RunAsync(RuleContext context)
{
this.Logger.LogTrace("()");
this.CheckAndComputeStake(context);
await base.RunAsync(context).ConfigureAwait(false);
var posRuleContext = context as PosRuleContext;
await this.stakeChain.SetAsync(context.ValidationContext.ChainedHeader, posRuleContext.BlockStake).ConfigureAwait(false);
this.Logger.LogTrace("(-)");
await this.stakeChain.SetAsync(context.ValidationContext.ChainedHeaderToValidate, posRuleContext.BlockStake).ConfigureAwait(false);
}
/// <inheritdoc/>
......@@ -62,8 +55,6 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <inheritdoc />
public 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))
{
var posRuleContext = context as PosRuleContext;
......@@ -87,32 +78,27 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
ConsensusErrors.BadCoinbaseAmount.Throw();
}
}
this.Logger.LogTrace("(-)");
}
/// <inheritdoc />
public override void UpdateCoinView(RuleContext context, Transaction transaction)
{
this.Logger.LogTrace("()");
var posRuleContext = context as PosRuleContext;
UnspentOutputSet view = posRuleContext.UnspentOutputSet;
if (transaction.IsCoinStake)
{
posRuleContext.TotalCoinStakeValueIn = view.GetValueIn(transaction);
posRuleContext.CoinStakePrevOutputs = transaction.Inputs.ToDictionary(txin => txin, txin => view.GetOutputFor(txin));
}
base.UpdateUTXOSet(context, transaction);
this.Logger.LogTrace("(-)");
}
/// <inheritdoc />
public override void CheckMaturity(UnspentOutputs coins, int spendHeight)
{
this.Logger.LogTrace("({0}:'{1}/{2}',{3}:{4})", nameof(coins), coins.TransactionId, coins.Height, nameof(spendHeight), spendHeight);
base.CheckCoinbaseMaturity(coins, spendHeight);
if (coins.IsCoinstake)
......@@ -124,8 +110,6 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
ConsensusErrors.BadTransactionPrematureCoinstakeSpending.Throw();
}
}
this.Logger.LogTrace("(-)");
}
/// <summary>
......@@ -136,11 +120,13 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <exception cref="ConsensusErrors.SetStakeEntropyBitFailed">Thrown if failed to set stake entropy bit.</exception>
private void CheckAndComputeStake(RuleContext context)
{
this.Logger.LogTrace("()");
ChainedHeader chainedHeader = context.ValidationContext.ChainedHeaderToValidate;
Block block = context.ValidationContext.BlockToValidate;
ChainedHeader chainedHeader = context.ValidationContext.ChainedHeader;
Block block = context.ValidationContext.Block;
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.
......@@ -180,7 +166,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
// Compute stake modifier.
ChainedHeader prevChainedHeader = chainedHeader.Previous;
BlockStake blockStakePrev = prevChainedHeader == null ? null : this.stakeChain.Get(prevChainedHeader.HashBlock);
blockStake.StakeModifierV2 = this.stakeValidator.ComputeStakeModifierV2(prevChainedHeader, blockStakePrev, blockStake.IsProofOfWork() ? chainedHeader.HashBlock : blockStake.PrevoutStake.Hash);
blockStake.StakeModifierV2 = this.stakeValidator.ComputeStakeModifierV2(prevChainedHeader, blockStakePrev?.StakeModifierV2, blockStake.IsProofOfWork() ? chainedHeader.HashBlock : blockStake.PrevoutStake.Hash);
}
else if (chainedHeader.Height == lastCheckpointHeight)
{
......@@ -190,8 +176,6 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
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);
this.Logger.LogTrace("(-)[OK]");
}
/// <inheritdoc />
......
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
{
/// <inheritdoc />
[ExecutionRule]
public sealed class DeStreamPowCoinviewRule : DeStreamCoinViewRule
{
/// <summary>Consensus parameters.</summary>
private NBitcoin.Consensus consensus;
private IConsensus consensus;
/// <inheritdoc />
public override void Initialize()
{
this.Logger.LogTrace("()");
base.Initialize();
this.consensus = this.Parent.Network.Consensus;
this.Logger.LogTrace("(-)");
}
/// <inheritdoc/>
......@@ -34,16 +30,12 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <inheritdoc/>
public override void CheckBlockReward(RuleContext context, Money fees, int height, Block block)
{
this.Logger.LogTrace("()");
Money blockReward = fees + this.GetProofOfWorkReward(height);
if (block.Transactions[0].TotalOut > blockReward)
{
this.Logger.LogTrace("(-)[BAD_COINBASE_AMOUNT]");
ConsensusErrors.BadCoinbaseAmount.Throw();
}
this.Logger.LogTrace("(-)");
}
/// <inheritdoc/>
......@@ -68,6 +60,28 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
return subsidy;
}
/// <inheritdoc />
protected override bool IsTxFinal(Transaction transaction, RuleContext context)
{
if (transaction.IsCoinBase)
return true;
ChainedHeader index = context.ValidationContext.ChainedHeaderToValidate;
UnspentOutputSet view = (context as UtxoRuleContext).UnspentOutputSet;
var prevheights = new int[transaction.Inputs.Count];
// Check that transaction is BIP68 final.
// BIP68 lock checks (as opposed to nLockTime checks) must
// be in ConnectBlock because they require the UTXO set.
for (int i = 0; i < transaction.Inputs.Count; i++)
{
prevheights[i] = (int)view.AccessCoins(transaction.Inputs[i].PrevOut.Hash).Height;
}
return transaction.CheckSequenceLocks(prevheights, index, context.Flags.LockTimeFlags);
}
/// <inheritdoc/>
public override void CheckMaturity(UnspentOutputs coins, int spendHeight)
{
......
......@@ -3,7 +3,7 @@
/// <summary>
/// A class that holds consensus errors.
/// </summary>
public static class ConsensusErrors
public static partial class ConsensusErrors
{
public static readonly ConsensusError InvalidPrevTip = new ConsensusError("invalid-prev-tip", "invalid previous tip");
public static readonly ConsensusError HighHash = new ConsensusError("high-hash", "proof of work failed");
......
using Stratis.Bitcoin.Consensus;
namespace Stratis.Bitcoin.Features.Consensus
namespace Stratis.Bitcoin.Consensus
{
public static partial class ConsensusErrors
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment