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

CONSENSUS: dBFT 3.0 with double speakers model #3254

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
35 changes: 28 additions & 7 deletions src/Plugins/DBFTPlugin/Consensus/ConsensusContext.Get.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ private RecoveryMessage.ChangeViewPayloadCompact GetChangeViewPayloadCompact(Ext
};
}

private RecoveryMessage.PreCommitPayloadCompact GetPreCommitPayloadCompact(ExtensiblePayload payload)
{
PreCommit preCommit = GetMessage<PreCommit>(payload);
return new RecoveryMessage.PreCommitPayloadCompact
{
ViewNumber = preCommit.ViewNumber,
ValidatorIndex = preCommit.ValidatorIndex,
PreparationHash = preCommit.PreparationHash,
InvocationScript = payload.Witness.InvocationScript,
};
}

private RecoveryMessage.CommitPayloadCompact GetCommitPayloadCompact(ExtensiblePayload payload)
{
Commit message = GetMessage<Commit>(payload);
Expand All @@ -66,12 +78,21 @@ private RecoveryMessage.PreparationPayloadCompact GetPreparationPayloadCompact(E
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetPrimaryIndex(byte viewNumber)
public byte GetPriorityPrimaryIndex(byte viewNumber)
{
int p = ((int)Block.Index - viewNumber) % Validators.Length;
int p = ((int)Block[0].Index - viewNumber) % Validators.Length;
return p >= 0 ? (byte)p : (byte)(p + Validators.Length);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetFallbackPrimaryIndex(byte priorityPrimaryIndex)
{
if (Validators.Length <= 1) return priorityPrimaryIndex;
int p = ((int)Block[0].Index + 1) % (Validators.Length - 1);
p = p >= 0 ? (byte)p : (byte)(p + Validators.Length);
return p < priorityPrimaryIndex ? (byte)p : (byte)(p + 1);
}

public UInt160 GetSender(int index)
{
return Contract.CreateSignatureRedeemScript(Validators[index]).ToScriptHash();
Expand All @@ -80,18 +101,18 @@ public UInt160 GetSender(int index)
/// <summary>
/// Return the expected block size
/// </summary>
public int GetExpectedBlockSize()
public int GetExpectedBlockSize(uint pId)
{
return GetExpectedBlockSizeWithoutTransactions(Transactions.Count) + // Base size
Transactions.Values.Sum(u => u.Size); // Sum Txs
return GetExpectedBlockSizeWithoutTransactions(Transactions[pId].Count) + // Base size
Transactions[pId].Values.Sum(u => u.Size); // Sum Txs
}

/// <summary>
/// Return the expected block system fee
/// </summary>
public long GetExpectedBlockSystemFee()
public long GetExpectedBlockSystemFee(uint pId)
{
return Transactions.Values.Sum(u => u.SystemFee); // Sum Txs
return Transactions[pId].Values.Sum(u => u.SystemFee); // Sum Txs
Copy link
Member

Choose a reason for hiding this comment

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

I think you should add some kind of error TryCatch to catch if KeyNotFound or InvalidIndex on pId

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you create a PR trying to improve that? Pointing to this branch?

Copy link
Member

Choose a reason for hiding this comment

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

I can, will be later though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

What should the value be when it errors?

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Lets say this method crashes cause of IndexOutOfRangeException than what?

Copy link
Member Author

Choose a reason for hiding this comment

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

Transactions[pID] = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash);

Copy link
Member Author

Choose a reason for hiding this comment

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

This loop here

for (uint pID = 0; pID <= 1; pID++)
{
if (ViewNumber > 0 && pID > 0) break;
if (reader.ReadUInt32() != Block[pID].Version) throw new FormatException();
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);
}
}

}

/// <summary>
Expand Down
97 changes: 62 additions & 35 deletions src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,18 @@
});
}

public ExtensiblePayload MakeCommit()
public ExtensiblePayload MakeCommit(uint pId)
{
return CommitPayloads[MyIndex] ?? (CommitPayloads[MyIndex] = MakeSignedPayload(new Commit
return CommitPayloads[pId][MyIndex] ?? (CommitPayloads[pId][MyIndex] = MakeSignedPayload(new Commit
{
Signature = EnsureHeader().Sign(keyPair, neoSystem.Settings.Network)
Signature = EnsureHeader(pId).Sign(keyPair, neoSystem.Settings.Network),
PId = pId
}));
}

private ExtensiblePayload MakeSignedPayload(ConsensusMessage message)
{
message.BlockIndex = Block.Index;
message.BlockIndex = Block[0].Index;
message.ValidatorIndex = (byte)MyIndex;
message.ViewNumber = ViewNumber;
ExtensiblePayload payload = CreatePayload(message, null);
Expand Down Expand Up @@ -71,16 +72,16 @@
/// Prevent that block exceed the max size
/// </summary>
/// <param name="txs">Ordered transactions</param>
internal void EnsureMaxBlockLimitation(IEnumerable<Transaction> txs)
internal void EnsureMaxBlockLimitation(IEnumerable<Transaction> txs, uint pId)

Check warning on line 75 in src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs

View workflow job for this annotation

GitHub Actions / Test (windows-latest)

Parameter 'pId' has no matching param tag in the XML comment for 'ConsensusContext.EnsureMaxBlockLimitation(IEnumerable<Transaction>, uint)' (but other parameters do)

Check warning on line 75 in src/Plugins/DBFTPlugin/Consensus/ConsensusContext.MakePayload.cs

View workflow job for this annotation

GitHub Actions / Test (macos-latest)

Parameter 'pId' has no matching param tag in the XML comment for 'ConsensusContext.EnsureMaxBlockLimitation(IEnumerable<Transaction>, uint)' (but other parameters do)
{
uint maxTransactionsPerBlock = neoSystem.Settings.MaxTransactionsPerBlock;

// Limit Speaker proposal to the limit `MaxTransactionsPerBlock` or all available transactions of the mempool
txs = txs.Take((int)maxTransactionsPerBlock);

List<UInt256> hashes = new List<UInt256>();
Transactions = new Dictionary<UInt256, Transaction>();
VerificationContext = new TransactionVerificationContext();
List<UInt256> hashes = new();
Transactions[pId] = new Dictionary<UInt256, Transaction>();
VerificationContext[pId] = new TransactionVerificationContext();

// Expected block size
var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count());
Expand All @@ -98,25 +99,38 @@
if (blockSystemFee > dbftSettings.MaxBlockSystemFee) break;

hashes.Add(tx.Hash);
Transactions.Add(tx.Hash, tx);
VerificationContext.AddTransaction(tx);
Transactions[pId].Add(tx.Hash, tx);
VerificationContext[pId].AddTransaction(tx);
}

TransactionHashes = hashes.ToArray();
TransactionHashes[pId] = hashes.ToArray();
}

public ExtensiblePayload MakePrepareRequest()
internal IEnumerable<Transaction> PickTransactions()
{
EnsureMaxBlockLimitation(neoSystem.MemPool.GetSortedVerifiedTransactions());
Block.Header.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1);
Block.Header.Nonce = GetNonce();
return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareRequest
var verifiedTxes = neoSystem.MemPool.GetSortedVerifiedTransactions();
if (ViewNumber > 0 && LastProposal.Length > 0)
{
Version = Block.Version,
PrevHash = Block.PrevHash,
Timestamp = Block.Timestamp,
Nonce = Block.Nonce,
TransactionHashes = TransactionHashes
var txes = verifiedTxes.Where(p => LastProposal.Contains(p.Hash));
if (txes.Count() > LastProposal.Length / 2)
return txes;
}
return verifiedTxes;
}

public ExtensiblePayload MakePrepareRequest(uint pId)
{
EnsureMaxBlockLimitation(PickTransactions(), pId);
Block[pId].Header.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1);
Block[pId].Header.Nonce = GetNonce();

return PreparationPayloads[pId][MyIndex] = MakeSignedPayload(new PrepareRequest
{
Version = Block[pId].Version,
PrevHash = Block[pId].PrevHash,
Timestamp = Block[pId].Timestamp,
Nonce = Block[pId].Nonce,
TransactionHashes = TransactionHashes[pId]
});
}

Expand All @@ -131,38 +145,51 @@
public ExtensiblePayload MakeRecoveryMessage()
{
PrepareRequest prepareRequestMessage = null;
if (TransactionHashes != null)
uint pId = TransactionHashes[0] != null ? 0u : (TransactionHashes[1] != null ? 1u : 0u);
if (TransactionHashes[pId] != null)
{
prepareRequestMessage = new PrepareRequest
{
Version = Block.Version,
PrevHash = Block.PrevHash,
Version = Block[pId].Version,
PrevHash = Block[pId].PrevHash,
ViewNumber = ViewNumber,
Timestamp = Block.Timestamp,
Nonce = Block.Nonce,
BlockIndex = Block.Index,
ValidatorIndex = Block.PrimaryIndex,
TransactionHashes = TransactionHashes
Timestamp = Block[pId].Timestamp,
Nonce = Block[pId].Nonce,
BlockIndex = Block[pId].Index,
ValidatorIndex = Block[pId].PrimaryIndex,
TransactionHashes = TransactionHashes[pId]
};
}
return MakeSignedPayload(new RecoveryMessage
{
PId = pId,
ChangeViewMessages = LastChangeViewPayloads.Where(p => p != null).Select(p => GetChangeViewPayloadCompact(p)).Take(M).ToDictionary(p => p.ValidatorIndex),
PrepareRequestMessage = prepareRequestMessage,
// We only need a PreparationHash set if we don't have the PrepareRequest information.
PreparationHash = TransactionHashes == null ? PreparationPayloads.Where(p => p != null).GroupBy(p => GetMessage<PrepareResponse>(p).PreparationHash, (k, g) => new { Hash = k, Count = g.Count() }).OrderByDescending(p => p.Count).Select(p => p.Hash).FirstOrDefault() : null,
PreparationMessages = PreparationPayloads.Where(p => p != null).Select(p => GetPreparationPayloadCompact(p)).ToDictionary(p => p.ValidatorIndex),
PreparationHash = TransactionHashes[pId] == null ? PreparationPayloads[pId].Where(p => p != null).GroupBy(p => GetMessage<PrepareResponse>(p).PreparationHash, (k, g) => new { Hash = k, Count = g.Count() }).OrderByDescending(p => p.Count).Select(p => p.Hash).FirstOrDefault() : null,
PreparationMessages = PreparationPayloads[pId].Where(p => p != null).Select(p => GetPreparationPayloadCompact(p)).ToDictionary(p => p.ValidatorIndex),
PreCommitMessages = PreCommitPayloads[pId].Where(p => p != null).Select(p => GetPreCommitPayloadCompact(p)).ToDictionary(p => p.ValidatorIndex),
CommitMessages = CommitSent
? CommitPayloads.Where(p => p != null).Select(p => GetCommitPayloadCompact(p)).ToDictionary(p => p.ValidatorIndex)
? CommitPayloads[pId].Where(p => p != null).Select(p => GetCommitPayloadCompact(p)).ToDictionary(p => p.ValidatorIndex)
: new Dictionary<byte, RecoveryMessage.CommitPayloadCompact>()
});
}

public ExtensiblePayload MakePrepareResponse()
public ExtensiblePayload MakePrepareResponse(uint pId)
{
return PreparationPayloads[pId][MyIndex] = MakeSignedPayload(new PrepareResponse
{
PreparationHash = PreparationPayloads[pId][Block[pId].PrimaryIndex].Hash,
PId = pId
});
}

public ExtensiblePayload MakePreCommit(uint pId)
{
return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareResponse
return PreCommitPayloads[pId][MyIndex] = MakeSignedPayload(new PreCommit
{
PreparationHash = PreparationPayloads[Block.PrimaryIndex].Hash
PreparationHash = PreparationPayloads[pId][Block[pId].PrimaryIndex].Hash,
PId = pId
});
}

Expand Down
Loading