Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/Nethermind/Nethermind.Xdc/RLP/VoteDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@ public void Encode(RlpStream stream, Vote item, RlpBehaviors rlpBehaviors = RlpB
stream.Encode(item.GapNumber);
}

public Rlp Encode(Vote item, RlpBehaviors rlpBehaviors = RlpBehaviors.None)
{
if (item is null)
return Rlp.OfEmptySequence;

RlpStream rlpStream = new(GetLength(item, rlpBehaviors));
Encode(rlpStream, item, rlpBehaviors);

return new Rlp(rlpStream.Data.ToArray());
}

public int GetLength(Vote item, RlpBehaviors rlpBehaviors)
{
return Rlp.LengthOfSequence(GetContentLength(item, rlpBehaviors));
Expand Down
4 changes: 3 additions & 1 deletion src/Nethermind/Nethermind.Xdc/Types/Timeout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

namespace Nethermind.Xdc.Types;

public class Timeout(ulong round, Signature? signature, ulong gapNumber)
public class Timeout(ulong round, Signature? signature, ulong gapNumber) : IXdcPoolItem
{
private readonly TimeoutDecoder _decoder = new();
private Address signer;

public ulong Round { get; set; } = round;
Expand All @@ -20,4 +21,5 @@ public class Timeout(ulong round, Signature? signature, ulong gapNumber)
public override string ToString() => $"{Round}:{GapNumber}";

public void SetSigner(Address signer) => this.signer = signer;
public (ulong Round, Hash256 hash) PoolKey() => (Round, Keccak.Compute(_decoder.Encode(this, RlpBehaviors.ForSealing).Bytes));
}
8 changes: 5 additions & 3 deletions src/Nethermind/Nethermind.Xdc/Types/Vote.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;
using Nethermind.Core.Crypto;
using System.Text.Json.Serialization;
using RlpBehaviors = Nethermind.Serialization.Rlp.RlpBehaviors;

namespace Nethermind.Xdc.Types;

public class Vote(BlockRoundInfo proposedBlockInfo, ulong gapNumber, Signature signature = null)
public class Vote(BlockRoundInfo proposedBlockInfo, ulong gapNumber, Signature signature = null) : IXdcPoolItem
{
private readonly VoteDecoder _decoder = new();
public BlockRoundInfo ProposedBlockInfo { get; set; } = proposedBlockInfo;
public ulong GapNumber { get; set; } = gapNumber;
public Signature? Signature { get; set; } = signature;

public override string ToString() =>
$"{ProposedBlockInfo.Round}:{GapNumber}:{ProposedBlockInfo.BlockNumber}";

public (ulong Round, Hash256 hash) PoolKey() => (ProposedBlockInfo.Round, Keccak.Compute(_decoder.Encode(this, RlpBehaviors.ForSealing).Bytes));
}
81 changes: 81 additions & 0 deletions src/Nethermind/Nethermind.Xdc/XdcPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections.Generic;
using System.Linq;
using Nethermind.Core.Collections;
using Nethermind.Core.Crypto;
using Nethermind.Core.Threading;

namespace Nethermind.Xdc;

public class XdcPool<T> where T : IXdcPoolItem
{
private readonly Dictionary<(ulong Round, Hash256 Hash), ArrayPoolList<T>> _items = new();
Comment thread
cicr99 marked this conversation as resolved.
private readonly McsLock _lock = new();

public long Add(T item)
{
using var lockRelease = _lock.Acquire();
{
var key = item.PoolKey();
if (!_items.TryGetValue(key, out var list))
{
//128 should be enough to cover all master nodes and some extras
list = new ArrayPoolList<T>(128);
_items[key] = list;
}
if (!list.Contains(item))
list.Add(item);
return list.Count;
}
}

public void EndRound(ulong round)
{
using var lockRelease = _lock.Acquire();
{
foreach (var key in _items.Keys)
{
if (key.Round <= round && _items.Remove(key, out ArrayPoolList<T> list))
{
list?.Dispose();
}
}
}
}

public IReadOnlyCollection<T> GetItems(T item)
{
using var lockRelease = _lock.Acquire();
{
var key = item.PoolKey();
if (_items.TryGetValue(key, out ArrayPoolList<T> list))
{
//Allocating a new array since it goes outside the lock
return list.ToArray();
}
return [];
}
}

public long GetCount(T item)
{
using var lockRelease = _lock.Acquire();
{
var key = item.PoolKey();
if (_items.TryGetValue(key, out ArrayPoolList<T> list))
{
return list.Count;
}
return 0;
}
}
}

public interface IXdcPoolItem
{
(ulong Round, Hash256 hash) PoolKey();
}