Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimize LastSeenMessage #1834

Merged
merged 9 commits into from
Aug 12, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
30 changes: 23 additions & 7 deletions src/neo/Consensus/ConsensusContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ internal class ConsensusContext : IDisposable, ISerializable
public ConsensusPayload[] LastChangeViewPayloads;
// 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 int[] LastSeenMessage;
public Dictionary<ECPoint, int> LastSeenMessage { get; private set; }

/// <summary>
/// Store all verified unsorted transactions' senders' fee currently in the consensus context.
Expand All @@ -55,7 +55,17 @@ internal class ConsensusContext : IDisposable, ISerializable
public bool WatchOnly => MyIndex < 0;
public Header PrevHeader => Snapshot.GetHeader(Block.PrevHash);
public int CountCommitted => CommitPayloads.Count(p => p != null);
public int CountFailed => LastSeenMessage.Count(p => p < (((int)Block.Index) - 1));
public int CountFailed => LastSeenMessage?.Count(p => p.Value < (((int)Block.Index) - 1)) ?? 0;
public bool ValidatorsChanged
{
get
{
if (Snapshot.Height == 0) return false;
TrimmedBlock currentBlock = Snapshot.Blocks[Snapshot.CurrentBlockHash];
TrimmedBlock previousBlock = Snapshot.Blocks[currentBlock.PrevHash];
return currentBlock.NextConsensus != previousBlock.NextConsensus;
}
}

#region Consensus States
public bool RequestSentOrReceived => PreparationPayloads[Block.ConsensusData.PrimaryIndex] != null;
Expand Down Expand Up @@ -384,11 +394,17 @@ public void Reset(byte viewNumber)
ChangeViewPayloads = new ConsensusPayload[Validators.Length];
LastChangeViewPayloads = new ConsensusPayload[Validators.Length];
CommitPayloads = new ConsensusPayload[Validators.Length];
if (LastSeenMessage == null)
if (ValidatorsChanged || LastSeenMessage is null)
{
LastSeenMessage = new int[Validators.Length];
for (int i = 0; i < Validators.Length; i++)
LastSeenMessage[i] = -1;
var previous_last_seen_message = LastSeenMessage;
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
LastSeenMessage = new Dictionary<ECPoint, int>();
foreach (var validator in Validators)
{
if (previous_last_seen_message != null && previous_last_seen_message.TryGetValue(validator, out int value))
LastSeenMessage[validator] = value;
else
LastSeenMessage[validator] = (int)Snapshot.Height;
}
}
keyPair = null;
for (int i = 0; i < Validators.Length; i++)
Expand Down Expand Up @@ -418,7 +434,7 @@ public void Reset(byte viewNumber)
Block.Transactions = null;
TransactionHashes = null;
PreparationPayloads = new ConsensusPayload[Validators.Length];
if (MyIndex >= 0) LastSeenMessage[MyIndex] = (int)Block.Index;
if (MyIndex >= 0) LastSeenMessage[Validators[MyIndex]] = (int)Block.Index;
}

public void Save()
Expand Down
2 changes: 1 addition & 1 deletion src/neo/Consensus/ConsensusService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ private void OnConsensusPayload(ConsensusPayload payload)
{
return;
}
context.LastSeenMessage[payload.ValidatorIndex] = (int)payload.BlockIndex;
context.LastSeenMessage[context.Validators[payload.ValidatorIndex]] = (int)payload.BlockIndex;
foreach (IP2PPlugin plugin in Plugin.P2PPlugins)
if (!plugin.OnConsensusMessage(payload))
return;
Expand Down
29 changes: 23 additions & 6 deletions tests/neo.UnitTests/Consensus/UT_Consensus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm
Console.WriteLine($"\n(UT-Consensus) Wallet is: {mockWallet.Object.GetAccount(UInt160.Zero).GetKey().PublicKey}");

var mockContext = new Mock<ConsensusContext>(mockWallet.Object, Blockchain.Singleton.Store);
mockContext.Object.LastSeenMessage = new int[] { 0, 0, 0, 0, 0, 0, 0 };

KeyPair[] kp_array = new KeyPair[7]
{
Expand Down Expand Up @@ -136,6 +135,10 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm
// As we may expect, as soon as consensus start it sends a RecoveryRequest of this aforementioned type
var askingForInitialRecovery = subscriber.ExpectMsg<LocalNode.SendDirectly>();
Console.WriteLine($"Recovery Message I: {askingForInitialRecovery}");
foreach (var validator in mockContext.Object.Validators)
{
mockContext.Object.LastSeenMessage[validator] = 0;
}
// Ensuring cast of type ConsensusPayload from the received message from subscriber
ConsensusPayload initialRecoveryPayload = (ConsensusPayload)askingForInitialRecovery.Inventory;
// Ensuring casting of type RecoveryRequest
Expand Down Expand Up @@ -166,7 +169,11 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm
mockContext.Object.ChangeViewPayloads[mockContext.Object.MyIndex] = null;
Console.WriteLine("Forcing Failed nodes for recovery request... ");
mockContext.Object.CountFailed.Should().Be(0);
mockContext.Object.LastSeenMessage = new int[] { -1, -1, -1, -1, -1, -1, -1 };
mockContext.Object.LastSeenMessage.Clear();
foreach (var validator in mockContext.Object.Validators)
{
mockContext.Object.LastSeenMessage[validator] = -1;
}
mockContext.Object.CountFailed.Should().Be(7);
Console.WriteLine("\nWaiting for recovery due to failed nodes... ");
var backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg<LocalNode.SendDirectly>();
Expand Down Expand Up @@ -266,6 +273,12 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm
kp_array[6].PublicKey
};
Console.WriteLine($"Generated keypairs PKey:");
//refresh LastSeenMessage
mockContext.Object.LastSeenMessage.Clear();
foreach (var item in mockContext.Object.Validators)
{
mockContext.Object.LastSeenMessage[item] = -1;
}
for (int i = 0; i < mockContext.Object.Validators.Length; i++)
Console.WriteLine($"{mockContext.Object.Validators[i]}/{Contract.CreateSignatureContract(mockContext.Object.Validators[i]).ScriptHash}");
var updatedContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators);
Expand Down Expand Up @@ -308,7 +321,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm
Console.WriteLine("\nAsserting CountCommitted is 2...");
mockContext.Object.CountCommitted.Should().Be(2);
Console.WriteLine($"\nAsserting CountFailed is 1...");
mockContext.Object.CountFailed.Should().Be(1);
mockContext.Object.CountFailed.Should().Be(6);

Console.WriteLine("\nCN6 simulation time");
TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 5, kp_array[5], updatedBlockHashData));
Expand All @@ -318,7 +331,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm
Console.WriteLine("\nAsserting CountCommitted is 3...");
mockContext.Object.CountCommitted.Should().Be(3);
Console.WriteLine($"\nAsserting CountFailed is 0...");
mockContext.Object.CountFailed.Should().Be(0);
mockContext.Object.CountFailed.Should().Be(5);

Console.WriteLine("\nCN5 simulation time");
TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 4, kp_array[4], updatedBlockHashData));
Expand Down Expand Up @@ -381,10 +394,14 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm
Console.WriteLine("\nAsserting CountCommitted is 0...");
mockContext.Object.CountCommitted.Should().Be(0);
Console.WriteLine($"\nAsserting CountFailed is 0...");
mockContext.Object.CountFailed.Should().Be(0);
mockContext.Object.CountFailed.Should().Be(3);
Console.WriteLine($"\nModifying CountFailed and asserting 7...");
// This will ensure a non-deterministic behavior after last recovery
mockContext.Object.LastSeenMessage = new int[] { -1, -1, -1, -1, -1, -1, -1 };
mockContext.Object.LastSeenMessage.Clear();
foreach (var validator in mockContext.Object.Validators)
{
mockContext.Object.LastSeenMessage[validator] = -1;
}
mockContext.Object.CountFailed.Should().Be(7);

TellConsensusPayload(actorConsensus, rmPayload);
Expand Down