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.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
......@@ -45,5 +46,32 @@ namespace Stratis.Bitcoin.Features.Consensus.Rules.CommonRules
// The task is not awaited so will not stall main validation process.
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
/// <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>
/// <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);
......
......@@ -23,7 +23,7 @@ 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.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(() =>
......
......@@ -838,7 +838,7 @@ namespace Stratis.Bitcoin.Features.MemoryPool
{
// Remove transactions which depend on inputs of tx, recursively
//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);
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