Commit 784621af authored by Sergei Zubov's avatar Sergei Zubov

Fix mempool corruption at zero-inputs

When node accepts transaction or receives block, it tries to load all
inputs to mempool. Trying to load zero-inputs to mempool leads to
corruption - valid transaction may be removed from mempool during
conflicts check. To prevent this, inputs selection algorithms modified
to ignore zero-inputs. Conflicts check ignores them too.
parent add3a1cb
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using NBitcoin; using NBitcoin;
...@@ -45,5 +46,32 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules ...@@ -45,5 +46,32 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
// The task is not awaited so will not stall main validation process. // The task is not awaited so will not stall main validation process.
this.TryPrefetchAsync(context.Flags); this.TryPrefetchAsync(context.Flags);
} }
/// <inheritdoc />
protected override uint256[] GetIdsToFetch(Block block, bool enforceBIP30)
{
this.Logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(block), block.GetHash(), nameof(enforceBIP30), enforceBIP30);
var ids = new HashSet<uint256>();
foreach (Transaction tx in block.Transactions)
{
if (enforceBIP30)
{
uint256 txId = tx.GetHash();
ids.Add(txId);
}
if (tx.IsCoinBase) continue;
foreach (TxIn input in tx.Inputs.RemoveChangePointer())
{
ids.Add(input.PrevOut.Hash);
}
}
uint256[] res = ids.ToArray();
this.Logger.LogTrace("(-):*.{0}={1}", nameof(res.Length), res.Length);
return res;
}
} }
} }
\ No newline at end of file
...@@ -58,7 +58,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules ...@@ -58,7 +58,7 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
/// <param name="block">The block with the transactions.</param> /// <param name="block">The block with the transactions.</param>
/// <param name="enforceBIP30">Whether to enforce look up of the transaction id itself and not only the reference to previous transaction id.</param> /// <param name="enforceBIP30">Whether to enforce look up of the transaction id itself and not only the reference to previous transaction id.</param>
/// <returns>A list of transaction ids to fetch from store</returns> /// <returns>A list of transaction ids to fetch from store</returns>
protected uint256[] GetIdsToFetch(Block block, bool enforceBIP30) protected virtual uint256[] GetIdsToFetch(Block block, bool enforceBIP30)
{ {
this.Logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(block), block.GetHash(), nameof(enforceBIP30), enforceBIP30); this.Logger.LogTrace("({0}:'{1}',{2}:{3})", nameof(block), block.GetHash(), nameof(enforceBIP30), enforceBIP30);
......
...@@ -23,7 +23,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool ...@@ -23,7 +23,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
public override async Task LoadViewAsync(Transaction trx) public override async Task LoadViewAsync(Transaction trx)
{ {
// lookup all ids (duplicate ids are ignored in case a trx spends outputs from the same parent). // 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(); 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()); FetchCoinsResponse coins = await this.Inner.FetchCoinsAsync(ids.ToArray());
// find coins currently in the mempool // find coins currently in the mempool
List<Transaction> mempoolcoins = await this.mempoolLock.ReadAsync(() => List<Transaction> mempoolcoins = await this.mempoolLock.ReadAsync(() =>
......
...@@ -838,7 +838,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool ...@@ -838,7 +838,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
{ {
// Remove transactions which depend on inputs of tx, recursively // Remove transactions which depend on inputs of tx, recursively
//LOCK(cs); //LOCK(cs);
foreach (TxIn txInput in tx.Inputs) foreach (TxIn txInput in tx.Inputs.RemoveChangePointer())
{ {
NextTxPair it = this.MapNextTx.FirstOrDefault(p => p.OutPoint == txInput.PrevOut); NextTxPair it = this.MapNextTx.FirstOrDefault(p => p.OutPoint == txInput.PrevOut);
if (it != null) if (it != null)
......
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