Commit 3854e8a3 authored by Sergei Zubov's avatar Sergei Zubov

Merge mempool

parent 1b7943dd
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>Stratis.Bitcoin.Features.MemoryPool.Tests</AssemblyName>
......@@ -14,19 +13,16 @@
<GenerateAssemblyCopyrightAttribute>false</GenerateAssemblyCopyrightAttribute>
<GenerateAssemblyVersionAttribute>false</GenerateAssemblyVersionAttribute>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<CodeAnalysisRuleSet>..\None.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRuleSet>..\None.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1705;IDE0008;</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Stratis.Bitcoin.Features.MemoryPool.Tests\**" />
<EmbeddedResource Remove="Stratis.Bitcoin.Features.MemoryPool.Tests\**" />
<None Remove="Stratis.Bitcoin.Features.MemoryPool.Tests\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DBreeze" Version="1.89.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
......@@ -35,7 +31,6 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NBitcoin\NBitcoin.csproj" />
<ProjectReference Include="..\Stratis.Bitcoin.Features.BlockStore\Stratis.Bitcoin.Features.BlockStore.csproj" />
......@@ -45,15 +40,12 @@
<ProjectReference Include="..\Stratis.Bitcoin.Tests.Common\Stratis.Bitcoin.Tests.Common.csproj" />
<ProjectReference Include="..\Stratis.Bitcoin\Stratis.Bitcoin.csproj" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
</Project>
\ No newline at end of file
......@@ -14,7 +14,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
public static class DeStreamFullNodeBuilderMemoryPoolExtension
{
/// <summary>
/// Include the memory pool feature and related services in the full node.
/// Include the memory pool feature and related services in the full node.
/// </summary>
/// <param name="fullNodeBuilder">Full node builder.</param>
/// <returns>Full node builder.</returns>
......
......@@ -11,11 +11,13 @@ using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.MemoryPool
{
/// <summary>
/// Creates <see cref="DeStreamUnspentOutputSet"/> and ChangePointer input
/// Creates <see cref="DeStreamUnspentOutputSet" /> and ChangePointer input
/// </summary>
public class DeStreamMempoolCoinView : MempoolCoinView
{
public DeStreamMempoolCoinView(CoinView inner, ITxMempool memPool, SchedulerLock mempoolLock, IMempoolValidator mempoolValidator) : base(inner, memPool, mempoolLock, mempoolValidator)
public DeStreamMempoolCoinView(ICoinView inner, ITxMempool memPool, SchedulerLock mempoolLock,
IMempoolValidator mempoolValidator)
: base(inner, memPool, mempoolLock, mempoolValidator)
{
this.Set = new DeStreamUnspentOutputSet();
}
......@@ -23,15 +25,21 @@ namespace Stratis.Bitcoin.Features.MemoryPool
public override async Task LoadViewAsync(Transaction trx)
{
// lookup all ids (duplicate ids are ignored in case a trx spends outputs from the same parent).
List<uint256> ids = trx.Inputs.RemoveChangePointer().Select(n => n.PrevOut.Hash).Distinct().Concat(new[] { trx.GetHash() }).ToList();
List<uint256> ids = trx.Inputs.RemoveChangePointer().Select(n => n.PrevOut.Hash).Distinct()
.Concat(new[] {trx.GetHash()}).ToList();
FetchCoinsResponse coins = await this.Inner.FetchCoinsAsync(ids.ToArray());
// find coins currently in the mempool
List<Transaction> mempoolcoins = await this.mempoolLock.ReadAsync(() =>
{
return this.memPool.MapTx.Values.Where(t => ids.Contains(t.TransactionHash)).Select(s => s.Transaction).ToList();
return this.memPool.MapTx.Values.Where(t => ids.Contains(t.TransactionHash))
.Select(s => s.Transaction).ToList();
});
IEnumerable<UnspentOutputs> memOutputs = mempoolcoins.Select(s => new UnspentOutputs(TxMempool.MempoolHeight, s));
coins = new FetchCoinsResponse(coins.UnspentOutputs.Concat(memOutputs).Append(new UnspentOutputs(uint256.Zero, new Coins(new Transaction(), 0))).ToArray(), coins.BlockHash);
IEnumerable<UnspentOutputs> memOutputs =
mempoolcoins.Select(s => new UnspentOutputs(TxMempool.MempoolHeight, s));
coins = new FetchCoinsResponse(
coins.UnspentOutputs.Concat(memOutputs)
.Append(new UnspentOutputs(uint256.Zero, new Coins(new Transaction(), 0))).ToArray(),
coins.BlockHash);
// the UTXO set might have been updated with a recently received block
// but the block has not yet arrived to the mempool and remove the pending trx
......
......@@ -8,22 +8,23 @@ using Stratis.Bitcoin.Utilities;
namespace Stratis.Bitcoin.Features.MemoryPool
{
/// <summary>
/// Creates <see cref="DeStreamMempoolCoinView"/>
/// Creates <see cref="DeStreamMempoolCoinView" />
/// </summary>
public class DeStreamMempoolManager : MempoolManager
{
public DeStreamMempoolManager(MempoolSchedulerLock mempoolLock, ITxMempool memPool, IMempoolValidator validator, IDateTimeProvider dateTimeProvider, MempoolSettings mempoolSettings, IMempoolPersistence mempoolPersistence, CoinView coinView, ILoggerFactory loggerFactory, Network network) : base(mempoolLock, memPool, validator, dateTimeProvider, mempoolSettings, mempoolPersistence, coinView, loggerFactory, network)
public DeStreamMempoolManager(MempoolSchedulerLock mempoolLock, ITxMempool memPool, IMempoolValidator validator,
IDateTimeProvider dateTimeProvider, MempoolSettings mempoolSettings, IMempoolPersistence mempoolPersistence,
ICoinView coinView, ILoggerFactory loggerFactory, Network network) : base(mempoolLock, memPool, validator,
dateTimeProvider, mempoolSettings, mempoolPersistence, coinView, loggerFactory, network)
{
}
public override async Task<UnspentOutputs> GetUnspentTransactionAsync(uint256 trxid)
{
TxMempoolInfo txInfo = await this.InfoAsync(trxid);
if (txInfo == null)
{
return null;
}
var memPoolCoinView = new DeStreamMempoolCoinView(this.coinView, this.memPool, this.MempoolLock, this.Validator);
TxMempoolInfo txInfo = await InfoAsync(trxid);
if (txInfo == null) return null;
var memPoolCoinView =
new DeStreamMempoolCoinView(this.coinView, this.memPool, this.MempoolLock, this.Validator);
await memPoolCoinView.LoadViewAsync(txInfo.Trx);
return memPoolCoinView.GetCoins(trxid);
}
......
......@@ -5,7 +5,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.Features.Consensus.CoinViews;
using Stratis.Bitcoin.Features.MemoryPool.Interfaces;
using Stratis.Bitcoin.Utilities;
......@@ -19,18 +19,28 @@ namespace Stratis.Bitcoin.Features.MemoryPool
{
public DeStreamMempoolValidator(ITxMempool memPool, MempoolSchedulerLock mempoolLock,
IDateTimeProvider dateTimeProvider, MempoolSettings mempoolSettings, ConcurrentChain chain,
CoinView coinView, ILoggerFactory loggerFactory, NodeSettings nodeSettings, IConsensusRules consensusRules)
: base(memPool, mempoolLock, dateTimeProvider, mempoolSettings, chain, coinView, loggerFactory,
nodeSettings, consensusRules)
ICoinView coinView, ILoggerFactory loggerFactory, NodeSettings nodeSettings,
IConsensusRuleEngine consensusRules) : base(memPool, mempoolLock, dateTimeProvider, mempoolSettings, chain,
coinView, loggerFactory, nodeSettings, consensusRules)
{
}
private DeStreamNetwork DeStreamNetwork
{
get
{
if (!(this.network is DeStreamNetwork))
throw new NotSupportedException($"Network must be {nameof(NBitcoin.DeStreamNetwork)}");
return (DeStreamNetwork) this.network;
}
}
protected override async Task AcceptToMemoryPoolWorkerAsync(MempoolValidationState state, Transaction tx,
List<uint256> vHashTxnToUncache)
{
var context = new MempoolValidationContext(tx, state);
this.PreMempoolChecks(context);
PreMempoolChecks(context);
// create the MemPoolCoinView and load relevant utxoset
context.View = new DeStreamMempoolCoinView(this.coinView, this.memPool, this.mempoolLock, this);
......@@ -45,33 +55,31 @@ namespace Stratis.Bitcoin.Features.MemoryPool
state.Invalid(MempoolErrors.InPool).Throw();
// Check for conflicts with in-memory transactions
this.CheckConflicts(context);
CheckConflicts(context);
this.CheckMempoolCoinView(context);
CheckMempoolCoinView(context);
this.CreateMempoolEntry(context, state.AcceptTime);
this.CheckSigOps(context);
this.CheckFee(context);
CreateMempoolEntry(context, state.AcceptTime);
CheckSigOps(context);
CheckFee(context);
this.CheckRateLimit(context, state.LimitFree);
CheckRateLimit(context, state.LimitFree);
this.CheckAncestors(context);
this.CheckReplacment(context);
this.CheckAllInputs(context);
CheckAncestors(context);
CheckReplacment(context);
CheckAllInputs(context);
// Remove conflicting transactions from the mempool
foreach (TxMempoolEntry it in context.AllConflicting)
{
this.logger.LogInformation(
$"replacing tx {it.TransactionHash} with {context.TransactionHash} for {context.ModifiedFees - context.ConflictingFees} BTC additional fees, {context.EntrySize - context.ConflictingSize} delta bytes");
}
this.memPool.RemoveStaged(context.AllConflicting, false);
// This transaction should only count for fee estimation if
// the node is not behind and it is not dependent on any other
// transactions in the mempool
bool validForFeeEstimation = this.IsCurrentForFeeEstimation() && this.memPool.HasNoInputsOf(tx);
bool validForFeeEstimation = IsCurrentForFeeEstimation() && this.memPool.HasNoInputsOf(tx);
// Store transaction in memory
this.memPool.AddUnchecked(context.TransactionHash, context.Entry, context.SetAncestors,
......@@ -80,7 +88,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
// trim mempool and check if tx was trimmed
if (!state.OverrideMempoolLimit)
{
this.LimitMempoolSize(this.mempoolSettings.MaxMempool * 1000000,
LimitMempoolSize(this.mempoolSettings.MaxMempool * 1000000,
this.mempoolSettings.MempoolExpiry * 60 * 60);
if (!this.memPool.Exists(context.TransactionHash))
......@@ -154,10 +162,10 @@ namespace Stratis.Bitcoin.Features.MemoryPool
{
TxMempool.NextTxPair itConflicting = this.memPool.MapNextTx.Find(f => f.OutPoint == txin.PrevOut);
if (itConflicting == null) continue;
Transaction ptxConflicting = itConflicting.Transaction;
if (context.SetConflicts.Contains(ptxConflicting.GetHash())) continue;
// Allow opt-out of transaction replacement by setting
// nSequence >= maxint-1 on all inputs.
//
......@@ -172,15 +180,13 @@ namespace Stratis.Bitcoin.Features.MemoryPool
// insecure.
bool replacementOptOut = true;
if (this.mempoolSettings.EnableReplacement)
{
foreach (TxIn txiner in ptxConflicting.Inputs)
{
if (txiner.Sequence >= Sequence.Final - 1) continue;
replacementOptOut = false;
break;
}
}
if (replacementOptOut)
context.State.Invalid(MempoolErrors.Conflict).Throw();
......@@ -188,12 +194,12 @@ namespace Stratis.Bitcoin.Features.MemoryPool
context.SetConflicts.Add(ptxConflicting.GetHash());
}
}
/// <summary>
/// Validates the transaction fee is valid.
/// Validates the transaction fee is valid.
/// </summary>
/// <param name="context">Current validation context.</param>
protected override void CheckFee(MempoolValidationContext context)
public override void CheckFee(MempoolValidationContext context)
{
long expectedFee = Convert.ToInt64(context.Transaction.Outputs
.Where(p => !context.Transaction.Inputs.RemoveChangePointer()
......@@ -201,8 +207,8 @@ namespace Stratis.Bitcoin.Features.MemoryPool
.Concat(context.Transaction.Inputs.GetChangePointers()
.Select(q => context.Transaction.Outputs[q].ScriptPubKey))
.Contains(p.ScriptPubKey))
.Sum(p => p.Value) * this.network.FeeRate);
.Sum(p => p.Value) * this.DeStreamNetwork.FeeRate);
if (context.ModifiedFees < expectedFee)
context.State.Fail(MempoolErrors.InsufficientFee, $" {context.Fees} < {expectedFee}").Throw();
}
......
......@@ -19,10 +19,10 @@ namespace Stratis.Bitcoin.Features.MemoryPool
{
/// <summary>Transaction memory pool for managing transactions in the memory pool.</summary>
/// <remarks>All access to this object has to be protected by <see cref="mempoolLock"/>.</remarks>
private readonly ITxMempool memPool;
protected readonly ITxMempool memPool;
/// <summary>A lock for protecting access to <see cref="memPool"/>.</summary>
private readonly SchedulerLock mempoolLock;
protected readonly SchedulerLock mempoolLock;
/// <summary>Memory pool validator for validating transactions.</summary>
private readonly IMempoolValidator mempoolValidator;
......@@ -46,7 +46,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// <summary>
/// Gets the unspent transaction output set.
/// </summary>
public UnspentOutputSet Set { get; private set; }
public UnspentOutputSet Set { get; protected set; }
/// <summary>
/// Backing coin view instance.
......@@ -87,7 +87,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Load the coin view for a memory pool transaction.
/// </summary>
/// <param name="trx">Memory pool transaction.</param>
public async Task LoadViewAsync(Transaction trx)
public virtual async Task LoadViewAsync(Transaction trx)
{
// lookup all ids (duplicate ids are ignored in case a trx spends outputs from the same parent).
List<uint256> ids = trx.Inputs.Select(n => n.PrevOut.Hash).Distinct().Concat(new[] { trx.GetHash() }).ToList();
......
......@@ -32,10 +32,10 @@ namespace Stratis.Bitcoin.Features.MemoryPool
private readonly ILogger logger;
/// <summary>Transaction memory pool for managing transactions in the memory pool.</summary>
private readonly ITxMempool memPool;
protected readonly ITxMempool memPool;
/// <summary>Coin view of the memory pool.</summary>
private readonly ICoinView coinView;
protected readonly ICoinView coinView;
private readonly Network network;
......@@ -217,7 +217,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
}
/// <inheritdoc />
public async Task<UnspentOutputs> GetUnspentTransactionAsync(uint256 trxid)
public virtual async Task<UnspentOutputs> GetUnspentTransactionAsync(uint256 trxid)
{
TxMempoolInfo txInfo = await this.InfoAsync(trxid);
if (txInfo == null)
......
......@@ -114,28 +114,28 @@ namespace Stratis.Bitcoin.Features.MemoryPool
private const int MaxFeeEstimationTipAge = 3 * 60 * 60;
/// <summary>A lock for managing asynchronous access to memory pool.</summary>
private readonly MempoolSchedulerLock mempoolLock;
protected readonly MempoolSchedulerLock mempoolLock;
/// <summary>Date and time information provider.</summary>
private readonly IDateTimeProvider dateTimeProvider;
/// <summary>Settings from the memory pool.</summary>
private readonly MempoolSettings mempoolSettings;
protected readonly MempoolSettings mempoolSettings;
/// <summary>Thread safe access to the best chain of block headers (that the node is aware of) from genesis.</summary>
private readonly ConcurrentChain chain;
/// <summary>Coin view of the memory pool.</summary>
private readonly ICoinView coinView;
protected readonly ICoinView coinView;
/// <inheritdoc cref="IConsensusRuleEngine" />
private readonly IConsensusRuleEngine consensusRules;
/// <summary>Transaction memory pool for managing transactions in the memory pool.</summary>
private readonly ITxMempool memPool;
protected readonly ITxMempool memPool;
/// <summary>Instance logger for memory pool validator.</summary>
private readonly ILogger logger;
protected readonly ILogger logger;
/// <summary>Minimum fee rate for a relay transaction.</summary>
private readonly FeeRate minRelayTxFee;
......@@ -152,7 +152,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
// public long LastTime;
//}
private Network network;
protected Network network;
public MempoolValidator(
ITxMempool memPool,
......@@ -413,7 +413,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// <param name="state">Validation state for creating the validation context.</param>
/// <param name="tx">The transaction to validate.</param>
/// <param name="vHashTxnToUncache">Not currently used</param>
private async Task AcceptToMemoryPoolWorkerAsync(MempoolValidationState state, Transaction tx, List<uint256> vHashTxnToUncache)
protected virtual async Task AcceptToMemoryPoolWorkerAsync(MempoolValidationState state, Transaction tx, List<uint256> vHashTxnToUncache)
{
var context = new MempoolValidationContext(tx, state);
......@@ -491,7 +491,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// If a conflict is found it is added to the validation context.
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckConflicts(MempoolValidationContext context)
protected virtual void CheckConflicts(MempoolValidationContext context)
{
context.SetConflicts = new List<uint256>();
foreach (TxIn txin in context.Transaction.Inputs)
......@@ -691,7 +691,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Checks if already in coin view, and missing and unavailable inputs.
/// </summary>
/// <param name="context">Validation context.</param>
private void CheckMempoolCoinView(MempoolValidationContext context)
protected virtual void CheckMempoolCoinView(MempoolValidationContext context)
{
Guard.Assert(context.View != null);
......@@ -758,7 +758,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Check that the transaction doesn't have an excessive number of sigops.
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckSigOps(MempoolValidationContext context)
protected void CheckSigOps(MempoolValidationContext context)
{
// Check that the transaction doesn't have an excessive number of
// sigops, making it impossible to mine. Since the coinbase transaction
......@@ -779,7 +779,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// </summary>
/// <param name="context">Current validation context.</param>
/// <param name="acceptTime">The accept time to use for the entry.</param>
private void CreateMempoolEntry(MempoolValidationContext context, long acceptTime)
protected void CreateMempoolEntry(MempoolValidationContext context, long acceptTime)
{
// Only accept BIP68 sequence locked transactions that can be mined in the next
// block; we don't want our mempool filled up with transactions that can't
......@@ -838,7 +838,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// The new transaction must have sufficient fees to pay for it's bandwidth.
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckReplacment(MempoolValidationContext context)
protected void CheckReplacment(MempoolValidationContext context)
{
// Check if it's economically rational to mine this transaction rather
// than the ones it replaces.
......@@ -969,7 +969,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// </summary>
/// <param name="context">Current validation context</param>
/// <param name="limitFree">Whether to limit free transactioins</param>
private void CheckRateLimit(MempoolValidationContext context, bool limitFree)
protected void CheckRateLimit(MempoolValidationContext context, bool limitFree)
{
// TODO: sort this logic
return;
......@@ -981,7 +981,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Checks for a transaction that spends outputs that would be replaced by it.
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckAncestors(MempoolValidationContext context)
protected void CheckAncestors(MempoolValidationContext context)
{
// Calculate in-mempool ancestors, up to a limit.
context.SetAncestors = new TxMempool.SetEntries();
......@@ -1020,7 +1020,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// </summary>
/// <param name="limit">New size.</param>
/// <param name="age">AAge to use for calculating expired transactions.</param>
private void LimitMempoolSize(long limit, long age)
protected void LimitMempoolSize(long limit, long age)
{
int expired = this.memPool.Expire(this.dateTimeProvider.GetTime() - age);
if (expired != 0)
......@@ -1035,7 +1035,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// It should only count for fee estimation if the node is not behind.
/// </summary>
/// <returns>Whether current for fee estimation.</returns>
private bool IsCurrentForFeeEstimation()
protected bool IsCurrentForFeeEstimation()
{
// TODO: implement method (find a way to know if in IBD)
......@@ -1058,7 +1058,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// Checks against <see cref="ScriptVerify.Standard"/> and <see cref="ScriptVerify.P2SH"/>
/// </summary>
/// <param name="context">Current validation context.</param>
private void CheckAllInputs(MempoolValidationContext context)
protected void CheckAllInputs(MempoolValidationContext context)
{
var scriptVerifyFlags = ScriptVerify.Standard;
if (!this.mempoolSettings.RequireStandard)
......@@ -1177,7 +1177,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
/// <param name="tx">Transaction to verify.</param>
/// <param name="mapInputs">Map of previous transactions that have outputs we're spending.</param>
/// <returns>Whether all inputs (scriptSigs) use only standard transaction forms.</returns>
private bool AreInputsStandard(Transaction tx, MempoolCoinView mapInputs)
protected virtual bool AreInputsStandard(Transaction tx, MempoolCoinView mapInputs)
{
if (tx.IsCoinBase)
{
......
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