Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
13 changes: 13 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM mcr.microsoft.com/dotnet/sdk:10.0.101-noble

# Install required dependencies for LevelDB and SQLite
RUN apt-get update && \
apt-get install -y \
libleveldb-dev \
sqlite3 \
libsqlite3-dev \
librocksdb-dev \
libsnappy-dev \
libunwind8-dev \
&& rm -rf /var/lib/apt/lists/*

21 changes: 21 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be moved to another PR later

"name": "Neo Node",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"workspaceFolder": "/workspace",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
"features": {},
"customizations": {
"vscode": {
"extensions": [
"ms-dotnettools.csharp",
"ms-dotnettools.csdevkit"
]
}
},
"remoteUser": "root",
"postCreateCommand": "dotnet restore"
}

235 changes: 151 additions & 84 deletions plugins/DBFTPlugin/Consensus/ConsensusContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,35 @@ public sealed partial class ConsensusContext : IDisposable, ISerializable
/// </summary>
private static readonly byte[] ConsensusStateKey = { 0xf4 };

public Block Block = null!;
//public Block Block = null!; Check if they should be set to null
public Block[] Block = new Block[2];
public byte ViewNumber;
public TimeSpan TimePerBlock;
public ECPoint[] Validators = null!;
public int MyIndex;
public UInt256[]? TransactionHashes;
public Dictionary<UInt256, Transaction>? Transactions;
public ExtensiblePayload?[] PreparationPayloads = null!;
public ExtensiblePayload?[] CommitPayloads = null!;

//Check the ? on Transaction and null! for the others
//public Dictionary<UInt256, Transaction>? Transactions;
//public ExtensiblePayload?[] PreparationPayloads = null!;
//public ExtensiblePayload?[] CommitPayloads = null!;
public UInt256[][] TransactionHashes = new UInt256[2][];
public Dictionary<UInt256, Transaction>[] Transactions = new Dictionary<UInt256, Transaction>[2];
public ExtensiblePayload[][] PreparationPayloads = new ExtensiblePayload[2][];
public ExtensiblePayload[][] PreCommitPayloads = new ExtensiblePayload[2][];
public ExtensiblePayload[][] CommitPayloads = new ExtensiblePayload[2][];

public ExtensiblePayload?[] ChangeViewPayloads = null!;
public ExtensiblePayload?[] LastChangeViewPayloads = null!;
public UInt256[] LastProposal;

// LastSeenMessage array stores the height of the last seen message, for each validator.
// if this node never heard from validator i, LastSeenMessage[i] will be -1.
public Dictionary<ECPoint, uint>? LastSeenMessage { get; private set; }

/// <summary>
/// Store all verified unsorted transactions' senders' fee currently in the consensus context.
/// </summary>
public TransactionVerificationContext VerificationContext = new();
public TransactionVerificationContext[] VerificationContext = new TransactionVerificationContext[2];

public StoreCache Snapshot { get; private set; } = null!;
private ECPoint? _myPublicKey;
Expand All @@ -62,17 +72,24 @@ public sealed partial class ConsensusContext : IDisposable, ISerializable

public int F => (Validators.Length - 1) / 3;
public int M => Validators.Length - F;
public bool IsPrimary => MyIndex == Block.PrimaryIndex;
public bool IsBackup => MyIndex >= 0 && MyIndex != Block.PrimaryIndex;
public bool IsPriorityPrimary => MyIndex == Block[0].PrimaryIndex;
public bool IsFallbackPrimary => ViewNumber == 0 && MyIndex == Block[1].PrimaryIndex;
public bool IsAPrimary => IsPriorityPrimary || IsFallbackPrimary;
//Modify to be 1 or 4/3
public static float PrimaryTimerPriorityMultiplier => 1;
public static float PrimaryTimerFallBackMultiplier => (float)4 / 3;
public float PrimaryTimerMultiplier => IsPriorityPrimary ? PrimaryTimerPriorityMultiplier : PrimaryTimerFallBackMultiplier;
public bool IsBackup => MyIndex >= 0 && !IsPriorityPrimary && !IsFallbackPrimary;

public bool WatchOnly => MyIndex < 0;
public Header PrevHeader => NativeContract.Ledger.GetHeader(Snapshot, Block.PrevHash)!;
public int CountCommitted => CommitPayloads.Count(p => p != null);
public Header PrevHeader => NativeContract.Ledger.GetHeader(Snapshot, Block[0].PrevHash)!;
public int CountCommitted => CommitPayloads[0].Count(p => p != null) + CommitPayloads[1].Count(p => p != null);
public int CountFailed
{
get
{
if (LastSeenMessage == null) return 0;
return Validators.Count(p => !LastSeenMessage.TryGetValue(p, out var value) || value < (Block.Index - 1));
return Validators.Count(p => !LastSeenMessage.TryGetValue(p, out var value) || value < (Block[0].Index - 1));
}
}
public bool ValidatorsChanged
Expand All @@ -88,10 +105,13 @@ public bool ValidatorsChanged
}

#region Consensus States
public bool RequestSentOrReceived => PreparationPayloads[Block.PrimaryIndex] != null;
public bool ResponseSent => !WatchOnly && PreparationPayloads[MyIndex] != null;
public bool CommitSent => !WatchOnly && CommitPayloads[MyIndex] != null;
public bool BlockSent => Block.Transactions != null;
public bool RequestSentOrReceived => PreparationPayloads[0][Block[0].PrimaryIndex] != null || (ViewNumber == 0 && PreparationPayloads[1][Block[1].PrimaryIndex] != null);
public bool ResponseSent => !WatchOnly && (PreparationPayloads[0][MyIndex] != null || (ViewNumber == 0 && PreparationPayloads[1][MyIndex] != null));
public bool PreCommitSent => !WatchOnly && (PreCommitPayloads[0][MyIndex] != null || (ViewNumber == 0 && PreCommitPayloads[1][MyIndex] != null));
public bool CommitSent => !WatchOnly && (CommitPayloads[0][MyIndex] != null || (ViewNumber == 0 && CommitPayloads[1][MyIndex] != null));
public bool BlockSent => Block[0].Transactions != null || Block[1]?.Transactions != null;


public bool ViewChanging => !WatchOnly && GetMessage<ChangeView>(ChangeViewPayloads[MyIndex])?.NewViewNumber > ViewNumber;
// NotAcceptingPayloadsDueToViewChanging imposes nodes to not accept some payloads if View is Changing,
// i.e: OnTransaction function will not process any transaction; OnPrepareRequestReceived will also return;
Expand Down Expand Up @@ -119,20 +139,20 @@ public ConsensusContext(NeoSystem neoSystem, DbftSettings settings, ISigner sign
store = neoSystem.LoadStore(settings.RecoveryLogs);
}

public Block CreateBlock()
public Block CreateBlock(uint pID)
{
EnsureHeader();
EnsureHeader(pID);
var contract = Contract.CreateMultiSigContract(M, Validators);
var sc = new ContractParametersContext(neoSystem.StoreView, Block.Header, dbftSettings.Network);
var sc = new ContractParametersContext(neoSystem.StoreView, Block[pID].Header, dbftSettings.Network);
for (int i = 0, j = 0; i < Validators.Length && j < M; i++)
{
if (GetMessage(CommitPayloads[i])?.ViewNumber != ViewNumber) continue;
sc.AddSignature(contract, Validators[i], GetMessage<Commit>(CommitPayloads[i]!).Signature.ToArray());
if (GetMessage(CommitPayloads[pID][i])?.ViewNumber != ViewNumber) continue;
sc.AddSignature(contract, Validators[i], GetMessage<Commit>(CommitPayloads[pID][i]!).Signature.ToArray());
j++;
}
Block.Header.Witness = sc.GetWitnesses()[0];
Block.Transactions = TransactionHashes!.Select(p => Transactions![p]).ToArray();
return Block;
Block[pID].Header.Witness = sc.GetWitnesses()[0];
Block[pID].Transactions = TransactionHashes!.Select(p => Transactions![p]).ToArray();
return Block[pID];
}

public ExtensiblePayload CreatePayload(ConsensusMessage message, ReadOnlyMemory<byte> invocationScript = default)
Expand All @@ -159,12 +179,11 @@ public void Dispose()
Snapshot?.Dispose();
}

public Block? EnsureHeader()
public Block? EnsureHeader(uint pID)
{
if (TransactionHashes == null) return null;
Block.Header.MerkleRoot ??= MerkleTree.ComputeRoot(TransactionHashes);
return Block;
}
Block[pID].Header.MerkleRoot ??= MerkleTree.ComputeRoot(TransactionHashes[pID]);
return Block[pID];

public bool Load()
{
Expand Down Expand Up @@ -195,21 +214,24 @@ public void Reset(byte viewNumber)
Snapshot?.Dispose();
Snapshot = neoSystem.GetSnapshotCache();
uint height = NativeContract.Ledger.CurrentIndex(Snapshot);
Block = new Block
for (uint i = 0; i <= 1; i++)
{
Header = new Header
Block[i] = new Block
{
PrevHash = NativeContract.Ledger.CurrentHash(Snapshot),
MerkleRoot = null!,
Index = height + 1,
NextConsensus = Contract.GetBFTAddress(
NeoToken.ShouldRefreshCommittee(height + 1, neoSystem.Settings.CommitteeMembersCount) ?
NativeContract.NEO.ComputeNextBlockValidators(Snapshot, neoSystem.Settings) :
NativeContract.NEO.GetNextBlockValidators(Snapshot, neoSystem.Settings.ValidatorsCount)),
Witness = null!
},
Transactions = null!
};
Header = new Header
{
PrevHash = NativeContract.Ledger.CurrentHash(Snapshot),
MerkleRoot = null!,
Index = height + 1,
NextConsensus = Contract.GetBFTAddress(
NeoToken.ShouldRefreshCommittee(height + 1, neoSystem.Settings.CommitteeMembersCount) ?
NativeContract.NEO.ComputeNextBlockValidators(Snapshot, neoSystem.Settings) :
NativeContract.NEO.GetNextBlockValidators(Snapshot, neoSystem.Settings.ValidatorsCount)),
Witness = null!
},
Transactions = null!
};
}
TimePerBlock = neoSystem.Settings.TimePerBlock;
var pv = Validators;
Validators = NativeContract.NEO.GetNextBlockValidators(Snapshot, neoSystem.Settings.ValidatorsCount);
Expand All @@ -231,7 +253,7 @@ public void Reset(byte viewNumber)
MyIndex = -1;
ChangeViewPayloads = new ExtensiblePayload[Validators.Length];
LastChangeViewPayloads = new ExtensiblePayload[Validators.Length];
CommitPayloads = new ExtensiblePayload[Validators.Length];

if (ValidatorsChanged || LastSeenMessage is null)
{
var previous_last_seen_message = LastSeenMessage;
Expand All @@ -255,6 +277,21 @@ public void Reset(byte viewNumber)
break;
}
cachedMessages = new Dictionary<UInt256, ConsensusMessage>();
LastProposal = Array.Empty<UInt256>();
for (uint pID = 0; pID <= 1; pID++)
{
Block[pID].Header.MerkleRoot = null;
Block[pID].Header.Timestamp = 0;
Block[pID].Header.Nonce = 0;
Block[pID].Transactions = null;
TransactionHashes[pID] = null;
PreparationPayloads[pID] = new ExtensiblePayload[Validators.Length];
PreCommitPayloads[pID] = new ExtensiblePayload[Validators.Length];
CommitPayloads[pID] = new ExtensiblePayload[Validators.Length];
if (MyIndex >= 0) LastSeenMessage[Validators[MyIndex]] = Block[pID].Index;
}
Block[0].Header.PrimaryIndex = GetPriorityPrimaryIndex(viewNumber);
Block[1].Header.PrimaryIndex = GetFallbackPrimaryIndex(Block[0].Header.PrimaryIndex);
}
else
{
Expand All @@ -263,16 +300,25 @@ public void Reset(byte viewNumber)
LastChangeViewPayloads[i] = ChangeViewPayloads[i];
else
LastChangeViewPayloads[i] = null;

Block[0].Header.MerkleRoot = null;
Block[0].Header.Timestamp = 0;
Block[0].Header.Nonce = 0;
Block[0].Transactions = null;
TransactionHashes[0] = null;
PreparationPayloads[0] = new ExtensiblePayload[Validators.Length];
PreCommitPayloads[0] = new ExtensiblePayload[Validators.Length];
if (MyIndex >= 0) LastSeenMessage[Validators[MyIndex]] = Block[0].Index;
Block[0].Header.PrimaryIndex = GetPriorityPrimaryIndex(viewNumber);

Block[1] = null;
TransactionHashes[1] = null;
Transactions[1] = null;
VerificationContext[1] = null;
PreparationPayloads[1] = null;
PreCommitPayloads[1] = null;
}
ViewNumber = viewNumber;
Block.Header.PrimaryIndex = GetPrimaryIndex(viewNumber);
Block.Header.MerkleRoot = null!;
Block.Header.Timestamp = 0;
Block.Header.Nonce = 0;
Block.Transactions = null!;
TransactionHashes = null;
PreparationPayloads = new ExtensiblePayload[Validators.Length];
if (MyIndex >= 0) LastSeenMessage![Validators[MyIndex]] = Block.Index;
}

public void Save()
Expand All @@ -283,50 +329,71 @@ public void Save()
public void Deserialize(ref MemoryReader reader)
{
Reset(0);

var blockVersion = reader.ReadUInt32();
if (blockVersion != Block.Version)
throw new FormatException($"Invalid block version: {blockVersion}/{Block.Version}");

if (reader.ReadUInt32() != Block.Index) throw new InvalidOperationException();
Block.Header.Timestamp = reader.ReadUInt64();
Block.Header.Nonce = reader.ReadUInt64();
Block.Header.PrimaryIndex = reader.ReadByte();
Block.Header.NextConsensus = reader.ReadSerializable<UInt160>();
if (Block.NextConsensus.Equals(UInt160.Zero))
Block.Header.NextConsensus = null!;
ViewNumber = reader.ReadByte();
TransactionHashes = reader.ReadSerializableArray<UInt256>(ushort.MaxValue);
Transaction[] transactions = reader.ReadSerializableArray<Transaction>(ushort.MaxValue);
PreparationPayloads = reader.ReadNullableArray<ExtensiblePayload>(neoSystem.Settings.ValidatorsCount);
CommitPayloads = reader.ReadNullableArray<ExtensiblePayload>(neoSystem.Settings.ValidatorsCount);
ChangeViewPayloads = reader.ReadNullableArray<ExtensiblePayload>(neoSystem.Settings.ValidatorsCount);
LastChangeViewPayloads = reader.ReadNullableArray<ExtensiblePayload>(neoSystem.Settings.ValidatorsCount);
if (TransactionHashes.Length == 0 && !RequestSentOrReceived)
TransactionHashes = null;
Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash);
VerificationContext = new TransactionVerificationContext();
if (Transactions != null)

for (uint pID = 0; pID <= 1; pID++)
{
foreach (Transaction tx in Transactions.Values)
VerificationContext.AddTransaction(tx);
if (ViewNumber > 0 && pID > 0) break;

var blockVersion = reader.ReadUInt32();
if (blockVersion != Block[pID].Version)
throw new FormatException($"Invalid block version: {blockVersion}/{Block[pID].Version}");
if (reader.ReadUInt32() != Block[pID].Index) throw new InvalidOperationException();

Block[pID].Header.Timestamp = reader.ReadUInt64();
Block[pID].Header.Nonce = reader.ReadUInt64();
Block[pID].Header.PrimaryIndex = reader.ReadByte();
Block[pID].Header.NextConsensus = reader.ReadSerializable<UInt160>();
if (Block[pID].NextConsensus.Equals(UInt160.Zero))
Block[pID].Header.NextConsensus = null!;

TransactionHashes[pID] = reader.ReadSerializableArray<UInt256>(ushort.MaxValue);
Transaction[] transactions = reader.ReadSerializableArray<Transaction>(ushort.MaxValue);
PreparationPayloads[pID] = reader.ReadNullableArray<ExtensiblePayload>(neoSystem.Settings.ValidatorsCount);
PreCommitPayloads[pID] = reader.ReadNullableArray<ExtensiblePayload>(neoSystem.Settings.ValidatorsCount);
CommitPayloads[pID] = reader.ReadNullableArray<ExtensiblePayload>(neoSystem.Settings.ValidatorsCount);

if (TransactionHashes[pID].Length == 0 && !RequestSentOrReceived)
TransactionHashes[pID] = null;
Transactions[pID] = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash);
VerificationContext[pID] = new TransactionVerificationContext();
if (Transactions[pID] != null)
{
foreach (Transaction tx in Transactions[pID].Values)
VerificationContext[pID].AddTransaction(tx);
}
}
ChangeViewPayloads[pID] = reader.ReadNullableArray<ExtensiblePayload>(neoSystem.Settings.ValidatorsCount);
LastChangeViewPayloads[pID] = reader.ReadNullableArray<ExtensiblePayload>(neoSystem.Settings.ValidatorsCount);
}

public void Serialize(BinaryWriter writer)
{
writer.Write(Block.Version);
writer.Write(Block.Index);
writer.Write(Block.Timestamp);
writer.Write(Block.Nonce);
writer.Write(Block.PrimaryIndex);
writer.Write(Block.NextConsensus ?? UInt160.Zero);

writer.Write(ViewNumber);
writer.Write(TransactionHashes ?? Array.Empty<UInt256>());
writer.Write(Transactions?.Values.ToArray() ?? Array.Empty<Transaction>());
writer.WriteNullableArray(PreparationPayloads);
writer.WriteNullableArray(CommitPayloads);
for (uint i = 0; i <= 1; i++)
{
if (ViewNumber > 0 && i > 0) break;
writer.Write(Block[i].Version);
writer.Write(Block[i].Index);
writer.Write(Block[i].Timestamp);
writer.Write(Block[i].Nonce);
writer.Write(Block[i].PrimaryIndex);
writer.Write(Block[i].NextConsensus ?? UInt160.Zero);
writer.Write(TransactionHashes[i] ?? Array.Empty<UInt256>());
writer.Write(Transactions[i]?.Values.ToArray() ?? Array.Empty<Transaction>());
writer.WriteNullableArray(PreparationPayloads[i]);
writer.WriteNullableArray(PreCommitPayloads[i]);
writer.WriteNullableArray(CommitPayloads[i]);
}

writer.WriteNullableArray(ChangeViewPayloads);
writer.WriteNullableArray(LastChangeViewPayloads);
}


private static void Log(string message, LogLevel level = LogLevel.Info)
{
Utility.Log(nameof(ConsensusService), level, message);
}
}
Loading
Loading