diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index b1ac6b33f5..3101c2d54d 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -13,6 +13,7 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using static Neo.Consensus.RecoveryMessage; namespace Neo.Consensus { @@ -29,10 +30,10 @@ public class ConsensusContext : IDisposable, ISerializable public int MyIndex; public UInt256[] TransactionHashes; public Dictionary Transactions; - public ConsensusPayload[] PreparationPayloads; - public ConsensusPayload[] CommitPayloads; - public ConsensusPayload[] ChangeViewPayloads; - public ConsensusPayload[] LastChangeViewPayloads; + public ExtensiblePayload[] PreparationPayloads; + public ExtensiblePayload[] CommitPayloads; + public ExtensiblePayload[] ChangeViewPayloads; + public ExtensiblePayload[] 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 Dictionary LastSeenMessage { get; private set; } @@ -47,6 +48,7 @@ public class ConsensusContext : IDisposable, ISerializable private int _witnessSize; private readonly Wallet wallet; private readonly IStore store; + private Dictionary cachedMessages; public int F => (Validators.Length - 1) / 3; public int M => Validators.Length - F; @@ -79,7 +81,7 @@ public bool ValidatorsChanged public bool ResponseSent => !WatchOnly && PreparationPayloads[MyIndex] != null; public bool CommitSent => !WatchOnly && CommitPayloads[MyIndex] != null; public bool BlockSent => Block.Transactions != null; - public bool ViewChanging => !WatchOnly && ChangeViewPayloads[MyIndex]?.GetDeserializedMessage().NewViewNumber > ViewNumber; + public bool ViewChanging => !WatchOnly && GetMessage(ChangeViewPayloads[MyIndex])?.NewViewNumber > ViewNumber; public bool NotAcceptingPayloadsDueToViewChanging => ViewChanging && !MoreThanFNodesCommittedOrLost; // A possible attack can happen if the last node to commit is malicious and either sends change view after his // commit to stall nodes in a higher view, or if he refuses to send recovery messages. In addition, if a node @@ -104,8 +106,8 @@ public Block CreateBlock() ContractParametersContext sc = new ContractParametersContext(Block); for (int i = 0, j = 0; i < Validators.Length && j < M; i++) { - if (CommitPayloads[i]?.ConsensusMessage.ViewNumber != ViewNumber) continue; - sc.AddSignature(contract, Validators[i], CommitPayloads[i].GetDeserializedMessage().Signature); + if (GetMessage(CommitPayloads[i])?.ViewNumber != ViewNumber) continue; + sc.AddSignature(contract, Validators[i], GetMessage(CommitPayloads[i]).Signature); j++; } Block.Witness = sc.GetWitnesses()[0]; @@ -113,6 +115,25 @@ public Block CreateBlock() return Block; } + public ExtensiblePayload CreatePayload(ConsensusMessage message, byte[] invocationScript = null) + { + ExtensiblePayload payload = new ExtensiblePayload + { + Category = "Consensus", + ValidBlockStart = 0, + ValidBlockEnd = message.BlockIndex, + Sender = GetSender(message.ValidatorIndex), + Data = message.ToArray(), + Witness = invocationScript is null ? null : new Witness + { + InvocationScript = invocationScript, + VerificationScript = Contract.CreateSignatureRedeemScript(Validators[message.ValidatorIndex]) + } + }; + cachedMessages.TryAdd(payload.Hash, message); + return payload; + } + public void Deserialize(BinaryReader reader) { Reset(0); @@ -126,10 +147,10 @@ public void Deserialize(BinaryReader reader) ViewNumber = reader.ReadByte(); TransactionHashes = reader.ReadSerializableArray(); Transaction[] transactions = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - PreparationPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - CommitPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - ChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - LastChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); + PreparationPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); + CommitPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); + ChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); + LastChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); if (TransactionHashes.Length == 0 && !RequestSentOrReceived) TransactionHashes = null; Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); @@ -154,6 +175,52 @@ public Block EnsureHeader() return Block; } + public ConsensusMessage GetMessage(ExtensiblePayload payload) + { + if (payload is null) return null; + if (!cachedMessages.TryGetValue(payload.Hash, out ConsensusMessage message)) + cachedMessages.Add(payload.Hash, message = ConsensusMessage.DeserializeFrom(payload.Data)); + return message; + } + + public T GetMessage(ExtensiblePayload payload) where T : ConsensusMessage + { + return (T)GetMessage(payload); + } + + private ChangeViewPayloadCompact GetChangeViewPayloadCompact(ExtensiblePayload payload) + { + ChangeView message = GetMessage(payload); + return new ChangeViewPayloadCompact + { + ValidatorIndex = message.ValidatorIndex, + OriginalViewNumber = message.ViewNumber, + Timestamp = message.Timestamp, + InvocationScript = payload.Witness.InvocationScript + }; + } + + private CommitPayloadCompact GetCommitPayloadCompact(ExtensiblePayload payload) + { + Commit message = GetMessage(payload); + return new CommitPayloadCompact + { + ViewNumber = message.ViewNumber, + ValidatorIndex = message.ValidatorIndex, + Signature = message.Signature, + InvocationScript = payload.Witness.InvocationScript + }; + } + + private PreparationPayloadCompact GetPreparationPayloadCompact(ExtensiblePayload payload) + { + return new PreparationPayloadCompact + { + ValidatorIndex = GetMessage(payload).ValidatorIndex, + InvocationScript = payload.Witness.InvocationScript + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte GetPrimaryIndex(byte viewNumber) { @@ -161,6 +228,11 @@ public byte GetPrimaryIndex(byte viewNumber) return p >= 0 ? (byte)p : (byte)(p + Validators.Length); } + public UInt160 GetSender(int index) + { + return Contract.CreateSignatureRedeemScript(Validators[index]).ToScriptHash(); + } + public bool Load() { byte[] data = store.TryGet(ConsensusStatePrefix, null); @@ -180,7 +252,7 @@ public bool Load() } } - public ConsensusPayload MakeChangeView(ChangeViewReason reason) + public ExtensiblePayload MakeChangeView(ChangeViewReason reason) { return ChangeViewPayloads[MyIndex] = MakeSignedPayload(new ChangeView { @@ -189,7 +261,7 @@ public ConsensusPayload MakeChangeView(ChangeViewReason reason) }); } - public ConsensusPayload MakeCommit() + public ExtensiblePayload MakeCommit() { return CommitPayloads[MyIndex] ?? (CommitPayloads[MyIndex] = MakeSignedPayload(new Commit { @@ -197,22 +269,17 @@ public ConsensusPayload MakeCommit() })); } - private ConsensusPayload MakeSignedPayload(ConsensusMessage message) + private ExtensiblePayload MakeSignedPayload(ConsensusMessage message) { + message.BlockIndex = Block.Index; + message.ValidatorIndex = (byte)MyIndex; message.ViewNumber = ViewNumber; - ConsensusPayload payload = new ConsensusPayload - { - Version = Block.Version, - PrevHash = Block.PrevHash, - BlockIndex = Block.Index, - ValidatorIndex = (byte)MyIndex, - ConsensusMessage = message - }; + ExtensiblePayload payload = CreatePayload(message, null); SignPayload(payload); return payload; } - private void SignPayload(ConsensusPayload payload) + private void SignPayload(ExtensiblePayload payload) { ContractParametersContext sc; try @@ -308,7 +375,7 @@ internal void EnsureMaxBlockLimitation(IEnumerable txs) TransactionHashes = hashes.ToArray(); } - public ConsensusPayload MakePrepareRequest() + public ExtensiblePayload MakePrepareRequest() { var random = new Random(); Span buffer = stackalloc byte[sizeof(ulong)]; @@ -319,13 +386,15 @@ public ConsensusPayload MakePrepareRequest() return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareRequest { + Version = Block.Version, + PrevHash = Block.PrevHash, Timestamp = Block.Timestamp, Nonce = Block.ConsensusData.Nonce, TransactionHashes = TransactionHashes }); } - public ConsensusPayload MakeRecoveryRequest() + public ExtensiblePayload MakeRecoveryRequest() { return MakeSignedPayload(new RecoveryRequest { @@ -333,33 +402,36 @@ public ConsensusPayload MakeRecoveryRequest() }); } - public ConsensusPayload MakeRecoveryMessage() + public ExtensiblePayload MakeRecoveryMessage() { PrepareRequest prepareRequestMessage = null; if (TransactionHashes != null) { prepareRequestMessage = new PrepareRequest { + Version = Block.Version, + PrevHash = Block.PrevHash, ViewNumber = ViewNumber, Timestamp = Block.Timestamp, + BlockIndex = Block.Index, Nonce = Block.ConsensusData.Nonce, TransactionHashes = TransactionHashes }; } return MakeSignedPayload(new RecoveryMessage() { - ChangeViewMessages = LastChangeViewPayloads.Where(p => p != null).Select(p => RecoveryMessage.ChangeViewPayloadCompact.FromPayload(p)).Take(M).ToDictionary(p => (int)p.ValidatorIndex), + ChangeViewMessages = LastChangeViewPayloads.Where(p => p != null).Select(p => GetChangeViewPayloadCompact(p)).Take(M).ToDictionary(p => (int)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 => p.GetDeserializedMessage().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 => RecoveryMessage.PreparationPayloadCompact.FromPayload(p)).ToDictionary(p => (int)p.ValidatorIndex), + PreparationHash = TransactionHashes == null ? PreparationPayloads.Where(p => p != null).GroupBy(p => GetMessage(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 => (int)p.ValidatorIndex), CommitMessages = CommitSent - ? CommitPayloads.Where(p => p != null).Select(p => RecoveryMessage.CommitPayloadCompact.FromPayload(p)).ToDictionary(p => (int)p.ValidatorIndex) - : new Dictionary() + ? CommitPayloads.Where(p => p != null).Select(p => GetCommitPayloadCompact(p)).ToDictionary(p => (int)p.ValidatorIndex) + : new Dictionary() }); } - public ConsensusPayload MakePrepareResponse() + public ExtensiblePayload MakePrepareResponse() { return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareResponse { @@ -401,9 +473,9 @@ public void Reset(byte viewNumber) } } MyIndex = -1; - ChangeViewPayloads = new ConsensusPayload[Validators.Length]; - LastChangeViewPayloads = new ConsensusPayload[Validators.Length]; - CommitPayloads = new ConsensusPayload[Validators.Length]; + 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; @@ -425,11 +497,12 @@ public void Reset(byte viewNumber) keyPair = account.GetKey(); break; } + cachedMessages = new Dictionary(); } else { for (int i = 0; i < LastChangeViewPayloads.Length; i++) - if (ChangeViewPayloads[i]?.GetDeserializedMessage().NewViewNumber >= viewNumber) + if (GetMessage(ChangeViewPayloads[i])?.NewViewNumber >= viewNumber) LastChangeViewPayloads[i] = ChangeViewPayloads[i]; else LastChangeViewPayloads[i] = null; @@ -443,7 +516,7 @@ public void Reset(byte viewNumber) Block.Timestamp = 0; Block.Transactions = null; TransactionHashes = null; - PreparationPayloads = new ConsensusPayload[Validators.Length]; + PreparationPayloads = new ExtensiblePayload[Validators.Length]; if (MyIndex >= 0) LastSeenMessage[Validators[MyIndex]] = Block.Index; } diff --git a/src/neo/Consensus/ConsensusMessage.cs b/src/neo/Consensus/ConsensusMessage.cs index 856ccdc72b..fceae7073a 100644 --- a/src/neo/Consensus/ConsensusMessage.cs +++ b/src/neo/Consensus/ConsensusMessage.cs @@ -8,9 +8,15 @@ namespace Neo.Consensus public abstract class ConsensusMessage : ISerializable { public readonly ConsensusMessageType Type; + public uint BlockIndex; + public byte ValidatorIndex; public byte ViewNumber; - public virtual int Size => sizeof(ConsensusMessageType) + sizeof(byte); + public virtual int Size => + sizeof(ConsensusMessageType) + //Type + sizeof(uint) + //BlockIndex + sizeof(byte) + //ValidatorIndex + sizeof(byte); //ViewNumber protected ConsensusMessage(ConsensusMessageType type) { @@ -23,6 +29,10 @@ public virtual void Deserialize(BinaryReader reader) { if (Type != (ConsensusMessageType)reader.ReadByte()) throw new FormatException(); + BlockIndex = reader.ReadUInt32(); + ValidatorIndex = reader.ReadByte(); + if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount) + throw new FormatException(); ViewNumber = reader.ReadByte(); } @@ -37,6 +47,8 @@ public static ConsensusMessage DeserializeFrom(byte[] data) public virtual void Serialize(BinaryWriter writer) { writer.Write((byte)Type); + writer.Write(BlockIndex); + writer.Write(ValidatorIndex); writer.Write(ViewNumber); } } diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 3f9f3d29b6..3361315e06 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -7,7 +7,7 @@ using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Plugins; +using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.Wallets; using System; @@ -134,7 +134,7 @@ private void ChangeTimer(TimeSpan delay) private void CheckCommits() { - if (context.CommitPayloads.Count(p => p?.ConsensusMessage.ViewNumber == context.ViewNumber) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) + if (context.CommitPayloads.Count(p => context.GetMessage(p)?.ViewNumber == context.ViewNumber) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) { block_received_index = context.Block.Index; block_received_time = TimeProvider.Current.UtcNow; @@ -147,12 +147,13 @@ private void CheckCommits() private void CheckExpectedView(byte viewNumber) { if (context.ViewNumber >= viewNumber) return; + var messages = context.ChangeViewPayloads.Select(p => context.GetMessage(p)).ToArray(); // if there are `M` change view payloads with NewViewNumber greater than viewNumber, then, it is safe to move - if (context.ChangeViewPayloads.Count(p => p != null && p.GetDeserializedMessage().NewViewNumber >= viewNumber) >= context.M) + if (messages.Count(p => p != null && p.NewViewNumber >= viewNumber) >= context.M) { if (!context.WatchOnly) { - ChangeView message = context.ChangeViewPayloads[context.MyIndex]?.GetDeserializedMessage(); + ChangeView message = messages[context.MyIndex]; // Communicate the network about my agreement to move to `viewNumber` // if my last change view payload, `message`, has NewViewNumber lower than current view to change if (message is null || message.NewViewNumber < viewNumber) @@ -166,7 +167,7 @@ private void CheckPreparations() { if (context.PreparationPayloads.Count(p => p != null) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) { - ConsensusPayload payload = context.MakeCommit(); + ExtensiblePayload payload = context.MakeCommit(); Log($"send commit"); context.Save(); localNode.Tell(new LocalNode.SendDirectly { Inventory = payload }); @@ -214,29 +215,29 @@ private void Log(string message, LogLevel level = LogLevel.Info) Utility.Log(nameof(ConsensusService), level, message); } - private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) + private void OnChangeViewReceived(ExtensiblePayload payload, ChangeView message) { if (message.NewViewNumber <= context.ViewNumber) - OnRecoveryRequestReceived(payload); + OnRecoveryRequestReceived(payload, message); if (context.CommitSent) return; - var expectedView = context.ChangeViewPayloads[payload.ValidatorIndex]?.GetDeserializedMessage().NewViewNumber ?? (byte)0; + var expectedView = context.GetMessage(context.ChangeViewPayloads[message.ValidatorIndex])?.NewViewNumber ?? 0; if (message.NewViewNumber <= expectedView) return; - Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber} reason={message.Reason}"); - context.ChangeViewPayloads[payload.ValidatorIndex] = payload; + Log($"{nameof(OnChangeViewReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex} nv={message.NewViewNumber} reason={message.Reason}"); + context.ChangeViewPayloads[message.ValidatorIndex] = payload; CheckExpectedView(message.NewViewNumber); } - private void OnCommitReceived(ConsensusPayload payload, Commit commit) + private void OnCommitReceived(ExtensiblePayload payload, Commit commit) { - ref ConsensusPayload existingCommitPayload = ref context.CommitPayloads[payload.ValidatorIndex]; + ref ExtensiblePayload existingCommitPayload = ref context.CommitPayloads[commit.ValidatorIndex]; if (existingCommitPayload != null) { if (existingCommitPayload.Hash != payload.Hash) - Log($"{nameof(OnCommitReceived)}: different commit from validator! height={payload.BlockIndex} index={payload.ValidatorIndex} view={commit.ViewNumber} existingView={existingCommitPayload.ConsensusMessage.ViewNumber}", LogLevel.Warning); + Log($"{nameof(OnCommitReceived)}: different commit from validator! height={commit.BlockIndex} index={commit.ValidatorIndex} view={commit.ViewNumber} existingView={context.GetMessage(existingCommitPayload).ViewNumber}", LogLevel.Warning); return; } @@ -246,14 +247,14 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit) if (commit.ViewNumber == context.ViewNumber) { - Log($"{nameof(OnCommitReceived)}: height={payload.BlockIndex} view={commit.ViewNumber} index={payload.ValidatorIndex} nc={context.CountCommitted} nf={context.CountFailed}"); + Log($"{nameof(OnCommitReceived)}: height={commit.BlockIndex} view={commit.ViewNumber} index={commit.ValidatorIndex} nc={context.CountCommitted} nf={context.CountFailed}"); byte[] hashData = context.EnsureHeader()?.GetHashData(); if (hashData == null) { existingCommitPayload = payload; } - else if (Crypto.VerifySignature(hashData, commit.Signature, context.Validators[payload.ValidatorIndex])) + else if (Crypto.VerifySignature(hashData, commit.Signature, context.Validators[commit.ValidatorIndex])) { existingCommitPayload = payload; CheckCommits(); @@ -261,7 +262,7 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit) return; } // Receiving commit from another view - Log($"{nameof(OnCommitReceived)}: record commit for different view={commit.ViewNumber} index={payload.ValidatorIndex} height={payload.BlockIndex}"); + Log($"{nameof(OnCommitReceived)}: record commit for different view={commit.ViewNumber} index={commit.ValidatorIndex} height={commit.BlockIndex}"); existingCommitPayload = payload; } @@ -273,23 +274,13 @@ private void ExtendTimerByFactor(int maxDelayInBlockTimes) ChangeTimer(nextDelay); } - private void OnConsensusPayload(ConsensusPayload payload) + private void OnConsensusPayload(ExtensiblePayload payload) { if (context.BlockSent) return; - if (payload.Version != context.Block.Version) return; - if (payload.PrevHash != context.Block.PrevHash || payload.BlockIndex != context.Block.Index) - { - if (context.Block.Index < payload.BlockIndex) - { - Log($"chain sync: expected={payload.BlockIndex} current={context.Block.Index - 1} nodes={LocalNode.Singleton.ConnectedCount}", LogLevel.Warning); - } - return; - } - if (payload.ValidatorIndex >= context.Validators.Length) return; ConsensusMessage message; try { - message = payload.ConsensusMessage; + message = context.GetMessage(payload); } catch (FormatException) { @@ -299,10 +290,17 @@ private void OnConsensusPayload(ConsensusPayload payload) { return; } - context.LastSeenMessage[context.Validators[payload.ValidatorIndex]] = payload.BlockIndex; - foreach (IP2PPlugin plugin in Plugin.P2PPlugins) - if (!plugin.OnConsensusMessage(payload)) - return; + if (message.BlockIndex != context.Block.Index) + { + if (context.Block.Index < message.BlockIndex) + { + Log($"chain sync: expected={message.BlockIndex} current={context.Block.Index - 1} nodes={LocalNode.Singleton.ConnectedCount}", LogLevel.Warning); + } + return; + } + if (message.ValidatorIndex >= context.Validators.Length) return; + if (payload.Sender != Contract.CreateSignatureRedeemScript(context.Validators[message.ValidatorIndex]).ToScriptHash()) return; + context.LastSeenMessage[context.Validators[message.ValidatorIndex]] = message.BlockIndex; switch (message) { case ChangeView view: @@ -317,11 +315,11 @@ private void OnConsensusPayload(ConsensusPayload payload) case Commit commit: OnCommitReceived(payload, commit); break; - case RecoveryRequest _: - OnRecoveryRequestReceived(payload); + case RecoveryRequest request: + OnRecoveryRequestReceived(payload, request); break; case RecoveryMessage recovery: - OnRecoveryMessageReceived(payload, recovery); + OnRecoveryMessageReceived(recovery); break; } } @@ -333,29 +331,29 @@ private void OnPersistCompleted(Block block) InitializeConsensus(0); } - private void OnRecoveryMessageReceived(ConsensusPayload payload, RecoveryMessage message) + private void OnRecoveryMessageReceived(RecoveryMessage message) { // isRecovering is always set to false again after OnRecoveryMessageReceived isRecovering = true; int validChangeViews = 0, totalChangeViews = 0, validPrepReq = 0, totalPrepReq = 0; int validPrepResponses = 0, totalPrepResponses = 0, validCommits = 0, totalCommits = 0; - Log($"{nameof(OnRecoveryMessageReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); + Log($"{nameof(OnRecoveryMessageReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex}"); try { if (message.ViewNumber > context.ViewNumber) { if (context.CommitSent) return; - ConsensusPayload[] changeViewPayloads = message.GetChangeViewPayloads(context, payload); + ExtensiblePayload[] changeViewPayloads = message.GetChangeViewPayloads(context); totalChangeViews = changeViewPayloads.Length; - foreach (ConsensusPayload changeViewPayload in changeViewPayloads) + foreach (ExtensiblePayload changeViewPayload in changeViewPayloads) if (ReverifyAndProcessPayload(changeViewPayload)) validChangeViews++; } if (message.ViewNumber == context.ViewNumber && !context.NotAcceptingPayloadsDueToViewChanging && !context.CommitSent) { if (!context.RequestSentOrReceived) { - ConsensusPayload prepareRequestPayload = message.GetPrepareRequestPayload(context, payload); + ExtensiblePayload prepareRequestPayload = message.GetPrepareRequestPayload(context); if (prepareRequestPayload != null) { totalPrepReq = 1; @@ -364,17 +362,17 @@ private void OnRecoveryMessageReceived(ConsensusPayload payload, RecoveryMessage else if (context.IsPrimary) SendPrepareRequest(); } - ConsensusPayload[] prepareResponsePayloads = message.GetPrepareResponsePayloads(context, payload); + ExtensiblePayload[] prepareResponsePayloads = message.GetPrepareResponsePayloads(context); totalPrepResponses = prepareResponsePayloads.Length; - foreach (ConsensusPayload prepareResponsePayload in prepareResponsePayloads) + foreach (ExtensiblePayload prepareResponsePayload in prepareResponsePayloads) if (ReverifyAndProcessPayload(prepareResponsePayload)) validPrepResponses++; } if (message.ViewNumber <= context.ViewNumber) { // Ensure we know about all commits from lower view numbers. - ConsensusPayload[] commitPayloads = message.GetCommitPayloadsFromRecoveryMessage(context, payload); + ExtensiblePayload[] commitPayloads = message.GetCommitPayloadsFromRecoveryMessage(context); totalCommits = commitPayloads.Length; - foreach (ConsensusPayload commitPayload in commitPayloads) + foreach (ExtensiblePayload commitPayload in commitPayloads) if (ReverifyAndProcessPayload(commitPayload)) validCommits++; } } @@ -389,7 +387,7 @@ private void OnRecoveryMessageReceived(ConsensusPayload payload, RecoveryMessage } } - private void OnRecoveryRequestReceived(ConsensusPayload payload) + private void OnRecoveryRequestReceived(ExtensiblePayload payload, ConsensusMessage message) { // We keep track of the payload hashes received in this block, and don't respond with recovery // in response to the same payload that we already responded to previously. @@ -399,7 +397,7 @@ private void OnRecoveryRequestReceived(ConsensusPayload payload) // additional recovery message response. if (!knownHashes.Add(payload.Hash)) return; - Log($"On{payload.ConsensusMessage.GetType().Name}Received: height={payload.BlockIndex} index={payload.ValidatorIndex} view={payload.ConsensusMessage.ViewNumber}"); + Log($"On{message.GetType().Name}Received: height={message.BlockIndex} index={message.ValidatorIndex} view={message.ViewNumber}"); if (context.WatchOnly) return; if (!context.CommitSent) { @@ -408,7 +406,7 @@ private void OnRecoveryRequestReceived(ConsensusPayload payload) // Limit recoveries to be sent from an upper limit of `f` nodes for (int i = 1; i <= allowedRecoveryNodeCount; i++) { - var chosenIndex = (payload.ValidatorIndex + i) % context.Validators.Length; + var chosenIndex = (message.ValidatorIndex + i) % context.Validators.Length; if (chosenIndex != context.MyIndex) continue; shouldSendRecovery = true; break; @@ -420,11 +418,12 @@ private void OnRecoveryRequestReceived(ConsensusPayload payload) localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryMessage() }); } - private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest message) + private void OnPrepareRequestReceived(ExtensiblePayload payload, PrepareRequest message) { if (context.RequestSentOrReceived || context.NotAcceptingPayloadsDueToViewChanging) return; - if (payload.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex || message.ViewNumber != context.ViewNumber) return; - Log($"{nameof(OnPrepareRequestReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} tx={message.TransactionHashes.Length}"); + if (message.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex || message.ViewNumber != context.ViewNumber) return; + if (message.Version != context.Block.Version || message.PrevHash != context.Block.PrevHash) return; + Log($"{nameof(OnPrepareRequestReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex} tx={message.TransactionHashes.Length}"); if (message.Timestamp <= context.PrevHeader.Timestamp || message.Timestamp > TimeProvider.Current.UtcNow.AddMilliseconds(8 * Blockchain.MillisecondsPerBlock).ToTimestampMS()) { Log($"Timestamp incorrect: {message.Timestamp}", LogLevel.Warning); @@ -447,13 +446,13 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m context.VerificationContext = new TransactionVerificationContext(); for (int i = 0; i < context.PreparationPayloads.Length; i++) if (context.PreparationPayloads[i] != null) - if (!context.PreparationPayloads[i].GetDeserializedMessage().PreparationHash.Equals(payload.Hash)) + if (!context.GetMessage(context.PreparationPayloads[i]).PreparationHash.Equals(payload.Hash)) context.PreparationPayloads[i] = null; - context.PreparationPayloads[payload.ValidatorIndex] = payload; + context.PreparationPayloads[message.ValidatorIndex] = payload; byte[] hashData = context.EnsureHeader().GetHashData(); for (int i = 0; i < context.CommitPayloads.Length; i++) - if (context.CommitPayloads[i]?.ConsensusMessage.ViewNumber == context.ViewNumber) - if (!Crypto.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage().Signature, context.Validators[i])) + if (context.GetMessage(context.CommitPayloads[i])?.ViewNumber == context.ViewNumber) + if (!Crypto.VerifySignature(hashData, context.GetMessage(context.CommitPayloads[i]).Signature, context.Validators[i])) context.CommitPayloads[i] = null; if (context.TransactionHashes.Length == 0) @@ -491,10 +490,10 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m } } - private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse message) + private void OnPrepareResponseReceived(ExtensiblePayload payload, PrepareResponse message) { if (message.ViewNumber != context.ViewNumber) return; - if (context.PreparationPayloads[payload.ValidatorIndex] != null || context.NotAcceptingPayloadsDueToViewChanging) return; + if (context.PreparationPayloads[message.ValidatorIndex] != null || context.NotAcceptingPayloadsDueToViewChanging) return; if (context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex] != null && !message.PreparationHash.Equals(context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex].Hash)) return; @@ -502,8 +501,8 @@ private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse // around 2*15/M=30.0/5 ~ 40% block time (for M=5) ExtendTimerByFactor(2); - Log($"{nameof(OnPrepareResponseReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); - context.PreparationPayloads[payload.ValidatorIndex] = payload; + Log($"{nameof(OnPrepareResponseReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex}"); + context.PreparationPayloads[message.ValidatorIndex] = payload; if (context.WatchOnly || context.CommitSent) return; if (context.RequestSentOrReceived) CheckPreparations(); @@ -534,7 +533,7 @@ protected override void OnReceive(object message) OnPersistCompleted(completed.Block); break; case Blockchain.RelayResult rr: - if (rr.Result == VerifyResult.Succeed && rr.Inventory is ConsensusPayload payload) + if (rr.Result == VerifyResult.Succeed && rr.Inventory is ExtensiblePayload payload && payload.Category == "Consensus") OnConsensusPayload(payload); break; } @@ -647,7 +646,7 @@ private void RequestChangeView(ChangeViewReason reason) CheckExpectedView(expectedView); } - private bool ReverifyAndProcessPayload(ConsensusPayload payload) + private bool ReverifyAndProcessPayload(ExtensiblePayload payload) { if (!payload.Verify(context.Snapshot)) return false; OnConsensusPayload(payload); @@ -682,7 +681,7 @@ internal protected override bool IsHighPriority(object message) { switch (message) { - case ConsensusPayload _: + case ExtensiblePayload _: case ConsensusService.SetViewNumber _: case ConsensusService.Timer _: case Blockchain.PersistCompleted _: diff --git a/src/neo/Consensus/PrepareRequest.cs b/src/neo/Consensus/PrepareRequest.cs index 2369b4f9f7..9e96ca4202 100644 --- a/src/neo/Consensus/PrepareRequest.cs +++ b/src/neo/Consensus/PrepareRequest.cs @@ -8,12 +8,16 @@ namespace Neo.Consensus { public class PrepareRequest : ConsensusMessage { + public uint Version; + public UInt256 PrevHash; public ulong Timestamp; public ulong Nonce; public UInt256[] TransactionHashes; public override int Size => base.Size - + sizeof(ulong) //Timestamp + + sizeof(uint) //Version + + UInt256.Length //PrevHash + + sizeof(ulong) //Timestamp + sizeof(ulong) //Nonce + TransactionHashes.GetVarSize(); //TransactionHashes @@ -25,6 +29,8 @@ public PrepareRequest() public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); + Version = reader.ReadUInt32(); + PrevHash = reader.ReadSerializable(); Timestamp = reader.ReadUInt64(); Nonce = reader.ReadUInt64(); TransactionHashes = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); @@ -35,6 +41,8 @@ public override void Deserialize(BinaryReader reader) public override void Serialize(BinaryWriter writer) { base.Serialize(writer); + writer.Write(Version); + writer.Write(PrevHash); writer.Write(Timestamp); writer.Write(Nonce); writer.Write(TransactionHashes); diff --git a/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs index 16d392fce5..8419193de6 100644 --- a/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Network.P2P.Payloads; using System; using System.IO; @@ -30,18 +29,6 @@ void ISerializable.Deserialize(BinaryReader reader) InvocationScript = reader.ReadVarBytes(1024); } - public static ChangeViewPayloadCompact FromPayload(ConsensusPayload payload) - { - ChangeView message = payload.GetDeserializedMessage(); - return new ChangeViewPayloadCompact - { - ValidatorIndex = payload.ValidatorIndex, - OriginalViewNumber = message.ViewNumber, - Timestamp = message.Timestamp, - InvocationScript = payload.Witness.InvocationScript - }; - } - void ISerializable.Serialize(BinaryWriter writer) { writer.Write(ValidatorIndex); diff --git a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs index 58754691da..81b3bc37af 100644 --- a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Network.P2P.Payloads; using System; using System.IO; @@ -30,18 +29,6 @@ void ISerializable.Deserialize(BinaryReader reader) InvocationScript = reader.ReadVarBytes(1024); } - public static CommitPayloadCompact FromPayload(ConsensusPayload payload) - { - Commit message = payload.GetDeserializedMessage(); - return new CommitPayloadCompact - { - ViewNumber = message.ViewNumber, - ValidatorIndex = payload.ValidatorIndex, - Signature = message.Signature, - InvocationScript = payload.Witness.InvocationScript - }; - } - void ISerializable.Serialize(BinaryWriter writer) { writer.Write(ViewNumber); diff --git a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs index f364c88385..72fbd2de2f 100644 --- a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Network.P2P.Payloads; using System; using System.IO; @@ -24,15 +23,6 @@ void ISerializable.Deserialize(BinaryReader reader) InvocationScript = reader.ReadVarBytes(1024); } - public static PreparationPayloadCompact FromPayload(ConsensusPayload payload) - { - return new PreparationPayloadCompact - { - ValidatorIndex = payload.ValidatorIndex, - InvocationScript = payload.Witness.InvocationScript - }; - } - void ISerializable.Serialize(BinaryWriter writer) { writer.Write(ValidatorIndex); diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs index 34e2e0da9a..11b8bd0361 100644 --- a/src/neo/Consensus/RecoveryMessage.cs +++ b/src/neo/Consensus/RecoveryMessage.cs @@ -1,6 +1,6 @@ using Neo.IO; using Neo.Network.P2P.Payloads; -using Neo.SmartContract; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -45,89 +45,47 @@ public override void Deserialize(BinaryReader reader) CommitMessages = reader.ReadSerializableArray(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); } - internal ConsensusPayload[] GetChangeViewPayloads(ConsensusContext context, ConsensusPayload payload) + internal ExtensiblePayload[] GetChangeViewPayloads(ConsensusContext context) { - return ChangeViewMessages.Values.Select(p => new ConsensusPayload + return ChangeViewMessages.Values.Select(p => context.CreatePayload(new ChangeView { - Version = payload.Version, - PrevHash = payload.PrevHash, - BlockIndex = payload.BlockIndex, + BlockIndex = BlockIndex, ValidatorIndex = p.ValidatorIndex, - ConsensusMessage = new ChangeView - { - ViewNumber = p.OriginalViewNumber, - Timestamp = p.Timestamp - }, - Witness = new Witness - { - InvocationScript = p.InvocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[p.ValidatorIndex]) - } - }).ToArray(); + ViewNumber = p.OriginalViewNumber, + Timestamp = p.Timestamp + }, p.InvocationScript)).ToArray(); } - internal ConsensusPayload[] GetCommitPayloadsFromRecoveryMessage(ConsensusContext context, ConsensusPayload payload) + internal ExtensiblePayload[] GetCommitPayloadsFromRecoveryMessage(ConsensusContext context) { - return CommitMessages.Values.Select(p => new ConsensusPayload + return CommitMessages.Values.Select(p => context.CreatePayload(new Commit { - Version = payload.Version, - PrevHash = payload.PrevHash, - BlockIndex = payload.BlockIndex, + BlockIndex = BlockIndex, ValidatorIndex = p.ValidatorIndex, - ConsensusMessage = new Commit - { - ViewNumber = p.ViewNumber, - Signature = p.Signature - }, - Witness = new Witness - { - InvocationScript = p.InvocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[p.ValidatorIndex]) - } - }).ToArray(); + ViewNumber = p.ViewNumber, + Signature = p.Signature + }, p.InvocationScript)).ToArray(); } - internal ConsensusPayload GetPrepareRequestPayload(ConsensusContext context, ConsensusPayload payload) + internal ExtensiblePayload GetPrepareRequestPayload(ConsensusContext context) { if (PrepareRequestMessage == null) return null; if (!PreparationMessages.TryGetValue(context.Block.ConsensusData.PrimaryIndex, out PreparationPayloadCompact compact)) return null; - return new ConsensusPayload - { - Version = payload.Version, - PrevHash = payload.PrevHash, - BlockIndex = payload.BlockIndex, - ValidatorIndex = context.Block.ConsensusData.PrimaryIndex, - ConsensusMessage = PrepareRequestMessage, - Witness = new Witness - { - InvocationScript = compact.InvocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[context.Block.ConsensusData.PrimaryIndex]) - } - }; + return context.CreatePayload(PrepareRequestMessage, compact.InvocationScript); } - internal ConsensusPayload[] GetPrepareResponsePayloads(ConsensusContext context, ConsensusPayload payload) + internal ExtensiblePayload[] GetPrepareResponsePayloads(ConsensusContext context) { UInt256 preparationHash = PreparationHash ?? context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex]?.Hash; - if (preparationHash is null) return new ConsensusPayload[0]; - return PreparationMessages.Values.Where(p => p.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex).Select(p => new ConsensusPayload + if (preparationHash is null) return Array.Empty(); + return PreparationMessages.Values.Where(p => p.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex).Select(p => context.CreatePayload(new PrepareResponse { - Version = payload.Version, - PrevHash = payload.PrevHash, - BlockIndex = payload.BlockIndex, + BlockIndex = BlockIndex, ValidatorIndex = p.ValidatorIndex, - ConsensusMessage = new PrepareResponse - { - ViewNumber = ViewNumber, - PreparationHash = preparationHash - }, - Witness = new Witness - { - InvocationScript = p.InvocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[p.ValidatorIndex]) - } - }).ToArray(); + ViewNumber = ViewNumber, + PreparationHash = preparationHash + }, p.InvocationScript)).ToArray(); } public override void Serialize(BinaryWriter writer) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 01898679f7..c475228b79 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -561,7 +561,7 @@ internal protected override bool IsHighPriority(object message) switch (message) { case Block _: - case ConsensusPayload _: + case ExtensiblePayload _: case Terminated _: return true; default: diff --git a/src/neo/Network/P2P/Message.cs b/src/neo/Network/P2P/Message.cs index ed0d23f883..15a7e0783a 100644 --- a/src/neo/Network/P2P/Message.cs +++ b/src/neo/Network/P2P/Message.cs @@ -38,7 +38,6 @@ public static Message Create(MessageCommand command, ISerializable payload = nul bool tryCompression = command == MessageCommand.Block || - command == MessageCommand.Consensus || command == MessageCommand.Extensible || command == MessageCommand.Transaction || command == MessageCommand.Headers || diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index 7438d90850..04856c4a48 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -38,8 +38,6 @@ public enum MessageCommand : byte Transaction = 0x2b, [ReflectionCache(typeof(Block))] Block = 0x2c, - [ReflectionCache(typeof(ConsensusPayload))] - Consensus = 0x2d, [ReflectionCache(typeof(ExtensiblePayload))] Extensible = 0x2e, Reject = 0x2f, diff --git a/src/neo/Network/P2P/Payloads/ConsensusPayload.cs b/src/neo/Network/P2P/Payloads/ConsensusPayload.cs deleted file mode 100644 index dbf2732fbc..0000000000 --- a/src/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Neo.Consensus; -using Neo.Cryptography; -using Neo.Cryptography.ECC; -using Neo.IO; -using Neo.Persistence; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using System; -using System.IO; - -namespace Neo.Network.P2P.Payloads -{ - public class ConsensusPayload : IInventory - { - public uint Version; - public UInt256 PrevHash; - public uint BlockIndex; - public byte ValidatorIndex; - public byte[] Data; - public Witness Witness; - - private ConsensusMessage _deserializedMessage = null; - public ConsensusMessage ConsensusMessage - { - get - { - if (_deserializedMessage is null) - _deserializedMessage = ConsensusMessage.DeserializeFrom(Data); - return _deserializedMessage; - } - internal set - { - if (!ReferenceEquals(_deserializedMessage, value)) - { - _deserializedMessage = value; - Data = value?.ToArray(); - } - } - } - - private UInt256 _hash = null; - public UInt256 Hash - { - get - { - if (_hash == null) - { - _hash = this.CalculateHash(); - } - return _hash; - } - } - - InventoryType IInventory.InventoryType => InventoryType.Consensus; - - public int Size => - sizeof(uint) + //Version - PrevHash.Size + //PrevHash - sizeof(uint) + //BlockIndex - sizeof(byte) + //ValidatorIndex - Data.GetVarSize() + //Data - 1 + Witness.Size; //Witness - - Witness[] IVerifiable.Witnesses - { - get - { - return new[] { Witness }; - } - set - { - if (value.Length != 1) throw new ArgumentException(); - Witness = value[0]; - } - } - - public T GetDeserializedMessage() where T : ConsensusMessage - { - return (T)ConsensusMessage; - } - - void ISerializable.Deserialize(BinaryReader reader) - { - ((IVerifiable)this).DeserializeUnsigned(reader); - if (reader.ReadByte() != 1) throw new FormatException(); - Witness = reader.ReadSerializable(); - } - - void IVerifiable.DeserializeUnsigned(BinaryReader reader) - { - Version = reader.ReadUInt32(); - PrevHash = reader.ReadSerializable(); - BlockIndex = reader.ReadUInt32(); - ValidatorIndex = reader.ReadByte(); - if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount) - throw new FormatException(); - Data = reader.ReadVarBytes(); - } - - UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot) - { - ECPoint[] validators = NativeContract.NEO.GetNextBlockValidators(snapshot); - if (validators.Length <= ValidatorIndex) - throw new InvalidOperationException(); - return new[] { Contract.CreateSignatureRedeemScript(validators[ValidatorIndex]).ToScriptHash() }; - } - - void ISerializable.Serialize(BinaryWriter writer) - { - ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write((byte)1); writer.Write(Witness); - } - - void IVerifiable.SerializeUnsigned(BinaryWriter writer) - { - writer.Write(Version); - writer.Write(PrevHash); - writer.Write(BlockIndex); - writer.Write(ValidatorIndex); - writer.WriteVarBytes(Data); - } - - public bool Verify(StoreView snapshot) - { - if (BlockIndex <= snapshot.Height) - return false; - return this.VerifyWitnesses(snapshot, 0_02000000); - } - } -} diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs index 6b21a81276..c5414770b2 100644 --- a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -64,7 +64,7 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) Category = reader.ReadVarString(32); ValidBlockStart = reader.ReadUInt32(); ValidBlockEnd = reader.ReadUInt32(); - if (ValidBlockStart > ValidBlockEnd) throw new FormatException(); + if (ValidBlockStart >= ValidBlockEnd) throw new FormatException(); Sender = reader.ReadSerializable(); Data = reader.ReadVarBytes(ushort.MaxValue); } diff --git a/src/neo/Network/P2P/Payloads/InventoryType.cs b/src/neo/Network/P2P/Payloads/InventoryType.cs index 33b9f2fc25..3e6bfa423f 100644 --- a/src/neo/Network/P2P/Payloads/InventoryType.cs +++ b/src/neo/Network/P2P/Payloads/InventoryType.cs @@ -4,7 +4,6 @@ public enum InventoryType : byte { TX = MessageCommand.Transaction, Block = MessageCommand.Block, - Extensible = MessageCommand.Extensible, - Consensus = MessageCommand.Consensus + Extensible = MessageCommand.Extensible } } diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 48e1cb3143..b0b1051c71 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -62,7 +62,6 @@ private void OnMessage(Message msg) OnAddrMessageReceived((AddrPayload)msg.Payload); break; case MessageCommand.Block: - case MessageCommand.Consensus: case MessageCommand.Extensible: OnInventoryReceived((IInventory)msg.Payload); break; diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index 8e1343ec0b..5fdc088b92 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -86,7 +86,6 @@ private void EnqueueMessage(Message message) switch (message.Command) { case MessageCommand.Alert: - case MessageCommand.Consensus: case MessageCommand.Extensible: case MessageCommand.FilterAdd: case MessageCommand.FilterClear: @@ -223,7 +222,6 @@ internal protected override bool IsHighPriority(object message) case Message msg: switch (msg.Command) { - case MessageCommand.Consensus: case MessageCommand.Extensible: case MessageCommand.FilterAdd: case MessageCommand.FilterClear: diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 688fa13cff..07088a9fb5 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -342,7 +342,7 @@ internal protected override bool IsHighPriority(object message) case TaskManager.RestartTasks _: return true; case TaskManager.NewTasks tasks: - if (tasks.Payload.Type == InventoryType.Block || tasks.Payload.Type == InventoryType.Consensus || tasks.Payload.Type == InventoryType.Extensible) + if (tasks.Payload.Type == InventoryType.Block || tasks.Payload.Type == InventoryType.Extensible) return true; return false; default: diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs index d6ba709e36..8323339d75 100644 --- a/src/neo/Plugins/IP2PPlugin.cs +++ b/src/neo/Plugins/IP2PPlugin.cs @@ -6,7 +6,6 @@ namespace Neo.Plugins public interface IP2PPlugin { bool OnP2PMessage(Message message) => true; - bool OnConsensusMessage(ConsensusPayload payload) => true; void OnVerifiedInventory(IInventory inventory); } } diff --git a/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs b/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs index dba8523f25..757d041801 100644 --- a/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs +++ b/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Consensus; using Neo.IO; -using Neo.Network.P2P.Payloads; namespace Neo.UnitTests.Consensus { @@ -29,18 +28,6 @@ public void DeserializeAndSerialize() Assert.AreEqual(test.ValidatorIndex, clone.ValidatorIndex); Assert.AreEqual(test.OriginalViewNumber, clone.OriginalViewNumber); CollectionAssert.AreEqual(test.InvocationScript, clone.InvocationScript); - - clone = RecoveryMessage.ChangeViewPayloadCompact.FromPayload(new ConsensusPayload() - { - Data = new ChangeView() { Timestamp = 1, ViewNumber = 3 }.ToArray(), - ValidatorIndex = 2, - Witness = new Witness() { InvocationScript = new byte[] { 1, 2, 3 } } - }); - - Assert.AreEqual(test.Timestamp, clone.Timestamp); - Assert.AreEqual(test.ValidatorIndex, clone.ValidatorIndex); - Assert.AreEqual(test.OriginalViewNumber, clone.OriginalViewNumber); - CollectionAssert.AreEqual(test.InvocationScript, clone.InvocationScript); } } } diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 85f066a675..a900f90797 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -28,15 +28,44 @@ namespace Neo.UnitTests.Consensus [TestClass] public class ConsensusTests : TestKit { + private KeyPair[] kp_array; + [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); + + var moked = new ECPoint[] { + ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", Neo.Cryptography.ECC.ECCurve.Secp256r1) + }; + + kp_array = new KeyPair[7] + { + UT_Crypto.generateKey(32), // not used, kept for index consistency, didactically + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32) + }.OrderBy(p => p.PublicKey).ToArray(); + + TestBlockchain.AddWhiteList(TestBlockchain.DefaultExtensibleWitnessWhiteList + .Concat(moked.Select(u => Contract.CreateSignatureContract(u).ScriptHash)) + .Concat(kp_array.Select(u => Contract.CreateSignatureContract(u.PublicKey).ScriptHash)) + .ToArray()); } [TestCleanup] public void Cleanup() { + TestBlockchain.AddWhiteList(TestBlockchain.DefaultExtensibleWitnessWhiteList); Shutdown(); } @@ -48,18 +77,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(mockWallet.Object, Blockchain.Singleton.Store); - - KeyPair[] kp_array = new KeyPair[7] - { - UT_Crypto.generateKey(32), // not used, kept for index consistency, didactically - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32) - }.OrderBy(p => p.PublicKey).ToArray(); - var timeValues = new[] { new DateTime(1980, 06, 01, 0, 0, 1, 001, DateTimeKind.Utc), // For tests, used below new DateTime(1980, 06, 01, 0, 0, 3, 001, DateTimeKind.Utc), // For receiving block @@ -67,7 +84,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm new DateTime(1980, 06, 01, 0, 0, 15, 001, DateTimeKind.Utc), // unused }; for (int i = 0; i < timeValues.Length; i++) - Console.WriteLine($"time {i}: {timeValues[i].ToString()} "); + Console.WriteLine($"time {i}: {timeValues[i]} "); ulong defaultTimestamp = 328665601001; // GMT: Sunday, June 1, 1980 12:00:01.001 AM // check basic ConsensusContext // mockConsensusContext.Object.block_received_time.ToTimestamp().Should().Be(4244941697); //1968-06-01 00:00:01 @@ -142,15 +159,15 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.LastSeenMessage[validator] = 0; } // Ensuring cast of type ConsensusPayload from the received message from subscriber - ConsensusPayload initialRecoveryPayload = (ConsensusPayload)askingForInitialRecovery.Inventory; + ExtensiblePayload initialRecoveryPayload = (ExtensiblePayload)askingForInitialRecovery.Inventory; // Ensuring casting of type RecoveryRequest - RecoveryRequest rrm = (RecoveryRequest)initialRecoveryPayload.ConsensusMessage; + RecoveryRequest rrm = initialRecoveryPayload.Data.AsSerializable(); rrm.Timestamp.Should().Be(defaultTimestamp); Console.WriteLine("Waiting for backup ChangeView... "); var backupOnAskingChangeView = subscriber.ExpectMsg(); - var changeViewPayload = (ConsensusPayload)backupOnAskingChangeView.Inventory; - ChangeView cvm = (ChangeView)changeViewPayload.ConsensusMessage; + var changeViewPayload = (ExtensiblePayload)backupOnAskingChangeView.Inventory; + ChangeView cvm = changeViewPayload.Data.AsSerializable(); cvm.Timestamp.Should().Be(defaultTimestamp); cvm.ViewNumber.Should().Be(0); cvm.Reason.Should().Be(ChangeViewReason.Timeout); @@ -175,42 +192,60 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(7); Console.WriteLine("\nWaiting for recovery due to failed nodes... "); var backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); - var recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; - rrm = (RecoveryRequest)recoveryPayload.ConsensusMessage; + var recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory; + rrm = recoveryPayload.Data.AsSerializable(); rrm.Timestamp.Should().Be(defaultTimestamp); Console.WriteLine("will create template MakePrepareRequest..."); mockContext.Object.PrevHeader.Timestamp = defaultTimestamp; mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0xe239c7228fa6b46cc0cf43623b2f934301d0b4f7")); var prepReq = mockContext.Object.MakePrepareRequest(); - var ppToSend = (PrepareRequest)prepReq.ConsensusMessage; + var ppToSend = prepReq.Data.AsSerializable(); // Forcing hashes to 0 because mempool is currently shared ppToSend.TransactionHashes = new UInt256[0]; ppToSend.TransactionHashes.Length.Should().Be(0); Console.WriteLine($"\nAsserting PreparationPayloads is 1 (After MakePrepareRequest)..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(1); - mockContext.Object.PreparationPayloads[prepReq.ValidatorIndex] = null; + mockContext.Object.PreparationPayloads[ppToSend.ValidatorIndex] = null; Console.WriteLine("will tell prepare request!"); + prepReq = new ExtensiblePayload + { + Category = "Consensus", + ValidBlockStart = 0, + ValidBlockEnd = ppToSend.BlockIndex, + Sender = prepReq.Sender, + Data = ppToSend.ToArray(), + Witness = prepReq.Witness + }; TellConsensusPayload(actorConsensus, prepReq); Console.WriteLine("Waiting for something related to the PrepRequest...\nNothing happens...Recovery will come due to failed nodes"); var backupOnRecoveryDueToFailedNodesII = subscriber.ExpectMsg(); - var recoveryPayloadII = (ConsensusPayload)backupOnRecoveryDueToFailedNodesII.Inventory; - rrm = (RecoveryRequest)recoveryPayloadII.ConsensusMessage; + var recoveryPayloadII = (ExtensiblePayload)backupOnRecoveryDueToFailedNodesII.Inventory; + rrm = recoveryPayloadII.Data.AsSerializable(); Console.WriteLine($"\nAsserting PreparationPayloads is 0..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(0); Console.WriteLine($"\nAsserting CountFailed is 6..."); mockContext.Object.CountFailed.Should().Be(6); Console.WriteLine("\nFailed because it is not primary and it created the prereq...Time to adjust"); - prepReq.ValidatorIndex = 1; //simulating primary as prepreq creator (signature is skip, no problem) + ppToSend.ValidatorIndex = 1; //simulating primary as prepreq creator (signature is skip, no problem) // cleaning old try with Self ValidatorIndex mockContext.Object.PreparationPayloads[mockContext.Object.MyIndex] = null; + prepReq = new ExtensiblePayload + { + Category = "Consensus", + ValidBlockStart = 0, + ValidBlockEnd = ppToSend.BlockIndex, + Sender = mockContext.Object.GetSender(ppToSend.ValidatorIndex), + Data = ppToSend.ToArray(), + Witness = prepReq.Witness + }; TellConsensusPayload(actorConsensus, prepReq); var OnPrepResponse = subscriber.ExpectMsg(); - var prepResponsePayload = (ConsensusPayload)OnPrepResponse.Inventory; - PrepareResponse prm = (PrepareResponse)prepResponsePayload.ConsensusMessage; + var prepResponsePayload = (ExtensiblePayload)OnPrepResponse.Inventory; + PrepareResponse prm = prepResponsePayload.Data.AsSerializable(); prm.PreparationHash.Should().Be(prepReq.Hash); Console.WriteLine("\nAsserting PreparationPayloads count is 2..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(2); @@ -218,11 +253,11 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(5); // Simulating CN 3 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 2)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 2)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); - recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; - rrm = (RecoveryRequest)recoveryPayload.ConsensusMessage; + recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory; + rrm = recoveryPayload.Data.AsSerializable(); rrm.Timestamp.Should().Be(defaultTimestamp); //Asserts Console.WriteLine("\nAsserting PreparationPayloads count is 3..."); @@ -231,11 +266,11 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(4); // Simulating CN 5 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 4)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 4)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); - recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; - rrm = (RecoveryRequest)recoveryPayload.ConsensusMessage; + recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory; + rrm = recoveryPayload.Data.AsSerializable(); rrm.Timestamp.Should().Be(defaultTimestamp); //Asserts Console.WriteLine("\nAsserting PreparationPayloads count is 4..."); @@ -244,10 +279,10 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(3); // Simulating CN 4 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 3)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 3)); var onCommitPayload = subscriber.ExpectMsg(); - var commitPayload = (ConsensusPayload)onCommitPayload.Inventory; - Commit cm = (Commit)commitPayload.ConsensusMessage; + var commitPayload = (ExtensiblePayload)onCommitPayload.Inventory; + Commit cm = commitPayload.Data.AsSerializable(); Console.WriteLine("\nAsserting PreparationPayloads count is 5..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(5); Console.WriteLine("\nAsserting CountCommitted is 1..."); @@ -303,36 +338,36 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm // Basic tests for understanding signatures and ensuring signatures of commits are correct on tests - var cmPayloadTemp = GetCommitPayloadModifiedAndSignedCopy(commitPayload, 6, kp_array[6], updatedBlockHashData); + var cmPayloadTemp = GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 6, kp_array[6], updatedBlockHashData); Crypto.VerifySignature(originalBlockHashData, cm.Signature, mockContext.Object.Validators[0]).Should().BeFalse(); Crypto.VerifySignature(updatedBlockHashData, cm.Signature, mockContext.Object.Validators[0]).Should().BeFalse(); - Crypto.VerifySignature(originalBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[6]).Should().BeFalse(); - Crypto.VerifySignature(updatedBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[6]).Should().BeTrue(); + Crypto.VerifySignature(originalBlockHashData, cmPayloadTemp.Data.AsSerializable().Signature, mockContext.Object.Validators[6]).Should().BeFalse(); + Crypto.VerifySignature(updatedBlockHashData, cmPayloadTemp.Data.AsSerializable().Signature, mockContext.Object.Validators[6]).Should().BeTrue(); Console.WriteLine("\n=========================="); Console.WriteLine("\n=========================="); Console.WriteLine("\nCN7 simulation time"); TellConsensusPayload(actorConsensus, cmPayloadTemp); var tempPayloadToBlockAndWait = subscriber.ExpectMsg(); - var rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; - RecoveryMessage rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + var rmPayload = (ExtensiblePayload)tempPayloadToBlockAndWait.Inventory; + RecoveryMessage rmm = rmPayload.Data.AsSerializable(); Console.WriteLine("\nAsserting CountCommitted is 2..."); mockContext.Object.CountCommitted.Should().Be(2); Console.WriteLine($"\nAsserting CountFailed is 1..."); mockContext.Object.CountFailed.Should().Be(6); Console.WriteLine("\nCN6 simulation time"); - TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 5, kp_array[5], updatedBlockHashData)); + TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 5, kp_array[5], updatedBlockHashData)); tempPayloadToBlockAndWait = subscriber.ExpectMsg(); - rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; - rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + rmPayload = (ExtensiblePayload)tempPayloadToBlockAndWait.Inventory; + rmm = rmPayload.Data.AsSerializable(); Console.WriteLine("\nAsserting CountCommitted is 3..."); mockContext.Object.CountCommitted.Should().Be(3); Console.WriteLine($"\nAsserting CountFailed is 0..."); mockContext.Object.CountFailed.Should().Be(5); Console.WriteLine("\nCN5 simulation time"); - TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 4, kp_array[4], updatedBlockHashData)); + TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 4, kp_array[4], updatedBlockHashData)); tempPayloadToBlockAndWait = subscriber.ExpectMsg(); Console.WriteLine("\nAsserting CountCommitted is 4..."); mockContext.Object.CountCommitted.Should().Be(4); @@ -341,11 +376,11 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm // Testing commit with wrong signature not valid // It will be invalid signature because we did not change ECPoint Console.WriteLine("\nCN4 simulation time. Wrong signature, KeyPair is not known"); - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(commitPayload, 3)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, commitPayload, 3)); Console.WriteLine("\nWaiting for recovery due to failed nodes... "); var backupOnRecoveryMessageAfterCommit = subscriber.ExpectMsg(); - rmPayload = (ConsensusPayload)backupOnRecoveryMessageAfterCommit.Inventory; - rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + rmPayload = (ExtensiblePayload)backupOnRecoveryMessageAfterCommit.Inventory; + rmm = rmPayload.Data.AsSerializable(); Console.WriteLine("\nAsserting CountCommitted is 4 (Again)..."); mockContext.Object.CountCommitted.Should().Be(4); Console.WriteLine("\nAsserting recovery message Preparation is 5..."); @@ -360,7 +395,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm var oldPrevHash = mockContext.Object.Block.PrevHash; mockContext.Object.Block.PrevHash = UInt256.Zero; //Payload should also be forced, otherwise OnConsensus will not pass - commitPayload.PrevHash = UInt256.Zero; Console.WriteLine($"\nNew Hash is {mockContext.Object.Block.GetHashData().ToScriptHash()}"); Console.WriteLine($"\nForcing block VerificationScript to {updatedContract.Script.ToScriptHash()}"); // The default behavior for BlockBase, when PrevHash = UInt256.Zero, is to use its own Witness @@ -370,7 +404,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine($"\nNew Hash is {mockContext.Object.Block.GetHashData().ToScriptHash()}"); Console.WriteLine("\nCN4 simulation time - Final needed signatures"); - TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData())); + TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData())); Console.WriteLine("\nWait for subscriber Block"); var utBlock = subscriber.ExpectMsg(); @@ -383,7 +417,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm // ============================================= Console.WriteLine("\nRecovery simulation..."); - mockContext.Object.CommitPayloads = new ConsensusPayload[mockContext.Object.Validators.Length]; + mockContext.Object.CommitPayloads = new ExtensiblePayload[mockContext.Object.Validators.Length]; // avoiding the BlockSent flag mockContext.Object.Block.Transactions = null; // ensuring same hash as snapshot @@ -402,8 +436,8 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("\nWaiting for RecoveryRequest before final asserts..."); var onRecoveryRequestAfterRecovery = subscriber.ExpectMsg(); - var rrPayload = (ConsensusPayload)onRecoveryRequestAfterRecovery.Inventory; - var rrMessage = (RecoveryRequest)rrPayload.ConsensusMessage; + var rrPayload = (ExtensiblePayload)onRecoveryRequestAfterRecovery.Inventory; + var rrMessage = rrPayload.Data.AsSerializable(); // It should be 3 because the commit generated by the default wallet is still invalid Console.WriteLine("\nAsserting CountCommitted is 3 (after recovery)..."); @@ -443,16 +477,18 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm /// new ValidatorIndex for the cpToCopy /// KeyPair that will be used for signing the Commit message used for creating blocks /// HashCode of the Block that is being produced and current being signed - public ConsensusPayload GetCommitPayloadModifiedAndSignedCopy(ConsensusPayload cpToCopy, byte vI, KeyPair kp, byte[] blockHashToSign) + public ExtensiblePayload GetCommitPayloadModifiedAndSignedCopy(ConsensusContext context, ExtensiblePayload cpToCopy, byte vI, KeyPair kp, byte[] blockHashToSign) { - var cpCommitTemp = cpToCopy.ToArray().AsSerializable(); - cpCommitTemp.ValidatorIndex = vI; - var oldViewNumber = ((Commit)cpCommitTemp.ConsensusMessage).ViewNumber; - cpCommitTemp.ConsensusMessage = new Commit + var cpCommitTemp = cpToCopy.ToArray().AsSerializable(); + cpCommitTemp.Sender = context.GetSender(vI); + var oldMessage = cpCommitTemp.Data.AsSerializable(); + cpCommitTemp.Data = new Commit { - ViewNumber = oldViewNumber, + BlockIndex = oldMessage.BlockIndex, + ValidatorIndex = vI, + ViewNumber = oldMessage.ViewNumber, Signature = Crypto.Sign(blockHashToSign, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()) - }; + }.ToArray(); SignPayload(cpCommitTemp, kp); return cpCommitTemp; } @@ -462,14 +498,17 @@ public ConsensusPayload GetCommitPayloadModifiedAndSignedCopy(ConsensusPayload c /// /// ConsensusPayload that will be modified /// new ValidatorIndex for the cpToCopy - public ConsensusPayload GetPayloadAndModifyValidator(ConsensusPayload cpToCopy, byte vI) + public ExtensiblePayload GetPayloadAndModifyValidator(ConsensusContext context, ExtensiblePayload cpToCopy, byte vI) { - var cpTemp = cpToCopy.ToArray().AsSerializable(); - cpTemp.ValidatorIndex = vI; + var cpTemp = cpToCopy.ToArray().AsSerializable(); + var message = ConsensusMessage.DeserializeFrom(cpTemp.Data); + message.ValidatorIndex = vI; + cpTemp.Data = message.ToArray(); + cpTemp.Sender = context.GetSender(vI); return cpTemp; } - private void SignPayload(ConsensusPayload payload, KeyPair kp) + private void SignPayload(ExtensiblePayload payload, KeyPair kp) { ContractParametersContext sc; try @@ -529,9 +568,10 @@ public void TestSerializeAndDeserializeConsensusContext() // consensusContext.TransactionHashes = new UInt256[2] {testTx1.Hash, testTx2.Hash}; consensusContext.Transactions = txs.ToDictionary(p => p.Hash); - consensusContext.PreparationPayloads = new ConsensusPayload[consensusContext.Validators.Length]; + consensusContext.PreparationPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; var prepareRequestMessage = new PrepareRequest { + PrevHash = consensusContext.Block.PrevHash, TransactionHashes = consensusContext.TransactionHashes, Timestamp = 23 }; @@ -543,7 +583,7 @@ public void TestSerializeAndDeserializeConsensusContext() consensusContext.PreparationPayloads[4] = null; consensusContext.PreparationPayloads[5] = null; - consensusContext.CommitPayloads = new ConsensusPayload[consensusContext.Validators.Length]; + consensusContext.CommitPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; using (SHA256 sha256 = SHA256.Create()) { consensusContext.CommitPayloads[3] = MakeSignedPayload(consensusContext, new Commit { Signature = sha256.ComputeHash(testTx1.Hash.ToArray()).Concat(sha256.ComputeHash(testTx1.Hash.ToArray())).ToArray() }, 3, new[] { (byte)'3', (byte)'4' }); @@ -552,7 +592,7 @@ public void TestSerializeAndDeserializeConsensusContext() consensusContext.Block.Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS(); - consensusContext.ChangeViewPayloads = new ConsensusPayload[consensusContext.Validators.Length]; + consensusContext.ChangeViewPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; consensusContext.ChangeViewPayloads[0] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 6 }, 0, new[] { (byte)'A' }); consensusContext.ChangeViewPayloads[1] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 5 }, 1, new[] { (byte)'B' }); consensusContext.ChangeViewPayloads[2] = null; @@ -561,7 +601,7 @@ public void TestSerializeAndDeserializeConsensusContext() consensusContext.ChangeViewPayloads[5] = null; consensusContext.ChangeViewPayloads[6] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 1 }, 6, new[] { (byte)'D' }); - consensusContext.LastChangeViewPayloads = new ConsensusPayload[consensusContext.Validators.Length]; + consensusContext.LastChangeViewPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; var copiedContext = TestUtils.CopyMsgBySerialization(consensusContext, new ConsensusContext(null, null)); @@ -727,6 +767,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR }, PrepareRequestMessage = new PrepareRequest { + PrevHash = UInt256.Zero, TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })), @@ -780,6 +821,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC ChangeViewMessages = new Dictionary(), PrepareRequestMessage = new PrepareRequest { + PrevHash = UInt256.Zero, TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationMessages = new Dictionary() @@ -840,6 +882,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm ChangeViewMessages = new Dictionary(), PrepareRequestMessage = new PrepareRequest { + PrevHash = UInt256.Zero, TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationMessages = new Dictionary() @@ -909,15 +952,17 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm copiedMsg.CommitMessages.Should().BeEquivalentTo(msg.CommitMessages); } - private static ConsensusPayload MakeSignedPayload(ConsensusContext context, ConsensusMessage message, byte validatorIndex, byte[] witnessInvocationScript) + private static ExtensiblePayload MakeSignedPayload(ConsensusContext context, ConsensusMessage message, byte validatorIndex, byte[] witnessInvocationScript) { - return new ConsensusPayload + message.BlockIndex = context.Block.Index; + message.ValidatorIndex = validatorIndex; + return new ExtensiblePayload { - Version = context.Block.Version, - PrevHash = context.Block.PrevHash, - BlockIndex = context.Block.Index, - ValidatorIndex = validatorIndex, - ConsensusMessage = message, + Category = "Consensus", + ValidBlockStart = 0, + ValidBlockEnd = message.BlockIndex, + Sender = context.GetSender(validatorIndex), + Data = message.ToArray(), Witness = new Witness { InvocationScript = witnessInvocationScript, @@ -937,7 +982,7 @@ private StorageKey CreateStorageKeyForNativeNeo(byte prefix) return storageKey; } - private void TellConsensusPayload(IActorRef actor, ConsensusPayload payload) + private void TellConsensusPayload(IActorRef actor, ExtensiblePayload payload) { actor.Tell(new Blockchain.RelayResult { diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs index 10984867b8..297ad0e17c 100644 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs +++ b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs @@ -12,7 +12,6 @@ namespace Neo.UnitTests.Consensus { - [TestClass] public class UT_ConsensusContext : TestKit { diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs index bde5ed0106..9de9617e65 100644 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs +++ b/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs @@ -34,7 +34,7 @@ public void TestSetup() public void ConsensusServiceMailbox_Test_IsHighPriority() { // high priority - uut.IsHighPriority(new ConsensusPayload()).Should().Be(true); + uut.IsHighPriority(new ExtensiblePayload()).Should().Be(true); uut.IsHighPriority(new ConsensusService.SetViewNumber()).Should().Be(true); uut.IsHighPriority(new ConsensusService.Timer()).Should().Be(true); uut.IsHighPriority(new Blockchain.PersistCompleted()).Should().Be(true); diff --git a/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs b/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs index de89ba2fb2..3c66f59533 100644 --- a/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs +++ b/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs @@ -12,7 +12,7 @@ public class UT_RecoveryRequest public void Size_Get() { var test = new RecoveryRequest() { Timestamp = 1, ViewNumber = 1 }; - test.Size.Should().Be(10); + test.Size.Should().Be(15); } [TestMethod] diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs index 5ae7aec0da..a5174d6cfe 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs @@ -57,7 +57,7 @@ public void RemoteNode_Test_IsHighPriority() uut.IsHighPriority(Message.Create(MessageCommand.NotFound, s)).Should().Be(false); uut.IsHighPriority(Message.Create(MessageCommand.Transaction, s)).Should().Be(false); uut.IsHighPriority(Message.Create(MessageCommand.Block, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.Consensus, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.Extensible, s)).Should().Be(true); uut.IsHighPriority(Message.Create(MessageCommand.Reject, s)).Should().Be(false); //SPV protocol @@ -153,7 +153,7 @@ public void ProtocolHandlerMailbox_Test_ShallDrop() uut.ShallDrop(msg, emptyQueue).Should().Be(false); uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Consensus (no drop) - msg = Message.Create(MessageCommand.Consensus, s); + msg = Message.Create(MessageCommand.Extensible, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Reject (no drop) diff --git a/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs index fca280d1eb..755bf979c1 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs @@ -43,7 +43,7 @@ public void TaskManager_Test_IsHighPriority() // high priority // -> NewTasks: payload Block or Consensus uut.IsHighPriority(new TaskManager.NewTasks { Payload = new InvPayload { Type = InventoryType.Block } }).Should().Be(true); - uut.IsHighPriority(new TaskManager.NewTasks { Payload = new InvPayload { Type = InventoryType.Consensus } }).Should().Be(true); + uut.IsHighPriority(new TaskManager.NewTasks { Payload = new InvPayload { Type = InventoryType.Extensible } }).Should().Be(true); // any random object should not have priority object obj = null; diff --git a/tests/neo.UnitTests/Plugins/UT_Plugin.cs b/tests/neo.UnitTests/Plugins/UT_Plugin.cs index de32a45898..2d520c0740 100644 --- a/tests/neo.UnitTests/Plugins/UT_Plugin.cs +++ b/tests/neo.UnitTests/Plugins/UT_Plugin.cs @@ -21,8 +21,6 @@ private class dummyPersistencePlugin : IPersistencePlugin { } public void TestIP2PPlugin() { var pp = new DummyP2PPlugin() as IP2PPlugin; - - Assert.IsTrue(pp.OnConsensusMessage(null)); Assert.IsTrue(pp.OnP2PMessage(null)); } diff --git a/tests/neo.UnitTests/TestBlockchain.cs b/tests/neo.UnitTests/TestBlockchain.cs index 786b2e7062..18d04454d3 100644 --- a/tests/neo.UnitTests/TestBlockchain.cs +++ b/tests/neo.UnitTests/TestBlockchain.cs @@ -1,11 +1,15 @@ using Neo.Ledger; using System; +using System.Collections.Immutable; +using System.Linq; +using System.Reflection; namespace Neo.UnitTests { public static class TestBlockchain { public static readonly NeoSystem TheNeoSystem; + public static readonly UInt160[] DefaultExtensibleWitnessWhiteList; static TestBlockchain() { @@ -14,11 +18,21 @@ static TestBlockchain() // Ensure that blockchain is loaded - var _ = Blockchain.Singleton; + var bc = Blockchain.Singleton; + + DefaultExtensibleWitnessWhiteList = (typeof(Blockchain).GetField("extensibleWitnessWhiteList", + BindingFlags.Instance | BindingFlags.NonPublic).GetValue(bc) as ImmutableHashSet).ToArray(); + AddWhiteList(DefaultExtensibleWitnessWhiteList); // Add other address } - public static void InitializeMockNeoSystem() + public static void InitializeMockNeoSystem() { } + + public static void AddWhiteList(params UInt160[] address) { + var builder = ImmutableHashSet.CreateBuilder(); + foreach (var entry in address) builder.Add(entry); + + typeof(Blockchain).GetField("extensibleWitnessWhiteList", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(Blockchain.Singleton, builder.ToImmutable()); } } }