diff --git a/src/neo/Consensus/ChangeView.cs b/src/neo/Consensus/ChangeView.cs
deleted file mode 100644
index 607e44cfd5..0000000000
--- a/src/neo/Consensus/ChangeView.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System.IO;
-
-namespace Neo.Consensus
-{
- public class ChangeView : ConsensusMessage
- {
- ///
- /// NewViewNumber is always set to the current ViewNumber asking changeview + 1
- ///
- public byte NewViewNumber => (byte)(ViewNumber + 1);
-
- ///
- /// Timestamp of when the ChangeView message was created. This allows receiving nodes to ensure
- /// they only respond once to a specific ChangeView request (it thus prevents replay of the ChangeView
- /// message from repeatedly broadcasting RecoveryMessages).
- ///
- public ulong Timestamp;
-
- ///
- /// Reason
- ///
- public ChangeViewReason Reason;
-
- public override int Size => base.Size +
- sizeof(ulong) + // Timestamp
- sizeof(ChangeViewReason); // Reason
-
- public ChangeView() : base(ConsensusMessageType.ChangeView) { }
-
- public override void Deserialize(BinaryReader reader)
- {
- base.Deserialize(reader);
- Timestamp = reader.ReadUInt64();
- Reason = (ChangeViewReason)reader.ReadByte();
- }
-
- public override void Serialize(BinaryWriter writer)
- {
- base.Serialize(writer);
- writer.Write(Timestamp);
- writer.Write((byte)Reason);
- }
- }
-}
diff --git a/src/neo/Consensus/ChangeViewReason.cs b/src/neo/Consensus/ChangeViewReason.cs
deleted file mode 100644
index 87c88c74c2..0000000000
--- a/src/neo/Consensus/ChangeViewReason.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace Neo.Consensus
-{
- public enum ChangeViewReason : byte
- {
- Timeout = 0x0,
- ChangeAgreement = 0x1,
- TxNotFound = 0x2,
- TxRejectedByPolicy = 0x3,
- TxInvalid = 0x4,
- BlockRejectedByPolicy = 0x5
- }
-}
diff --git a/src/neo/Consensus/Commit.cs b/src/neo/Consensus/Commit.cs
deleted file mode 100644
index fce9b8caaf..0000000000
--- a/src/neo/Consensus/Commit.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using Neo.IO;
-using System.IO;
-
-namespace Neo.Consensus
-{
- public class Commit : ConsensusMessage
- {
- public byte[] Signature;
-
- public override int Size => base.Size + Signature.Length;
-
- public Commit() : base(ConsensusMessageType.Commit) { }
-
- public override void Deserialize(BinaryReader reader)
- {
- base.Deserialize(reader);
- Signature = reader.ReadFixedBytes(64);
- }
-
- public override void Serialize(BinaryWriter writer)
- {
- base.Serialize(writer);
- writer.Write(Signature);
- }
- }
-}
diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs
deleted file mode 100644
index 3101c2d54d..0000000000
--- a/src/neo/Consensus/ConsensusContext.cs
+++ /dev/null
@@ -1,544 +0,0 @@
-using Neo.Cryptography;
-using Neo.Cryptography.ECC;
-using Neo.IO;
-using Neo.Ledger;
-using Neo.Network.P2P.Payloads;
-using Neo.Persistence;
-using Neo.SmartContract;
-using Neo.SmartContract.Native;
-using Neo.VM;
-using Neo.Wallets;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using static Neo.Consensus.RecoveryMessage;
-
-namespace Neo.Consensus
-{
- public class ConsensusContext : IDisposable, ISerializable
- {
- ///
- /// Key for saving consensus state.
- ///
- private const byte ConsensusStatePrefix = 0xf4;
-
- public Block Block;
- public byte ViewNumber;
- public ECPoint[] Validators;
- public int MyIndex;
- public UInt256[] TransactionHashes;
- public Dictionary Transactions;
- 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; }
-
- ///
- /// Store all verified unsorted transactions' senders' fee currently in the consensus context.
- ///
- public TransactionVerificationContext VerificationContext = new TransactionVerificationContext();
-
- public SnapshotView Snapshot { get; private set; }
- private KeyPair keyPair;
- 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;
- public bool IsPrimary => MyIndex == Block.ConsensusData.PrimaryIndex;
- public bool IsBackup => MyIndex >= 0 && MyIndex != Block.ConsensusData.PrimaryIndex;
- public bool WatchOnly => MyIndex < 0;
- public Header PrevHeader => Snapshot.GetHeader(Block.PrevHash);
- public int CountCommitted => CommitPayloads.Count(p => p != null);
- public int CountFailed
- {
- get
- {
- if (LastSeenMessage == null) return 0;
- return Validators.Count(p => !LastSeenMessage.TryGetValue(p, out var value) || value < (Block.Index - 1));
- }
- }
- public bool ValidatorsChanged
- {
- get
- {
- if (Snapshot.Height == 0) return false;
- TrimmedBlock currentBlock = Snapshot.Blocks[Snapshot.CurrentBlockHash];
- TrimmedBlock previousBlock = Snapshot.Blocks[currentBlock.PrevHash];
- return currentBlock.NextConsensus != previousBlock.NextConsensus;
- }
- }
-
- #region Consensus States
- public bool RequestSentOrReceived => PreparationPayloads[Block.ConsensusData.PrimaryIndex] != null;
- public bool ResponseSent => !WatchOnly && PreparationPayloads[MyIndex] != null;
- public bool CommitSent => !WatchOnly && CommitPayloads[MyIndex] != null;
- public bool BlockSent => Block.Transactions != null;
- 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
- // asking change views loses network or crashes and comes back when nodes are committed in more than one higher
- // numbered view, it is possible for the node accepting recovery to commit in any of the higher views, thus
- // potentially splitting nodes among views and stalling the network.
- public bool MoreThanFNodesCommittedOrLost => (CountCommitted + CountFailed) > F;
- #endregion
-
- public int Size => throw new NotImplementedException();
-
- public ConsensusContext(Wallet wallet, IStore store)
- {
- this.wallet = wallet;
- this.store = store;
- }
-
- public Block CreateBlock()
- {
- EnsureHeader();
- Contract contract = Contract.CreateMultiSigContract(M, Validators);
- ContractParametersContext sc = new ContractParametersContext(Block);
- for (int i = 0, j = 0; i < Validators.Length && j < M; i++)
- {
- if (GetMessage(CommitPayloads[i])?.ViewNumber != ViewNumber) continue;
- sc.AddSignature(contract, Validators[i], GetMessage(CommitPayloads[i]).Signature);
- j++;
- }
- Block.Witness = sc.GetWitnesses()[0];
- Block.Transactions = TransactionHashes.Select(p => Transactions[p]).ToArray();
- 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);
- if (reader.ReadUInt32() != Block.Version) throw new FormatException();
- if (reader.ReadUInt32() != Block.Index) throw new InvalidOperationException();
- Block.Timestamp = reader.ReadUInt64();
- Block.NextConsensus = reader.ReadSerializable();
- if (Block.NextConsensus.Equals(UInt160.Zero))
- Block.NextConsensus = null;
- Block.ConsensusData = reader.ReadSerializable();
- 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);
- if (TransactionHashes.Length == 0 && !RequestSentOrReceived)
- TransactionHashes = null;
- Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash);
- VerificationContext = new TransactionVerificationContext();
- if (Transactions != null)
- {
- foreach (Transaction tx in Transactions.Values)
- VerificationContext.AddTransaction(tx);
- }
- }
-
- public void Dispose()
- {
- Snapshot?.Dispose();
- }
-
- public Block EnsureHeader()
- {
- if (TransactionHashes == null) return null;
- if (Block.MerkleRoot is null)
- Block.MerkleRoot = Block.CalculateMerkleRoot(Block.ConsensusData.Hash, TransactionHashes);
- 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)
- {
- int p = ((int)Block.Index - viewNumber) % Validators.Length;
- 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);
- if (data is null || data.Length == 0) return false;
- using (MemoryStream ms = new MemoryStream(data, false))
- using (BinaryReader reader = new BinaryReader(ms))
- {
- try
- {
- Deserialize(reader);
- }
- catch
- {
- return false;
- }
- return true;
- }
- }
-
- public ExtensiblePayload MakeChangeView(ChangeViewReason reason)
- {
- return ChangeViewPayloads[MyIndex] = MakeSignedPayload(new ChangeView
- {
- Reason = reason,
- Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS()
- });
- }
-
- public ExtensiblePayload MakeCommit()
- {
- return CommitPayloads[MyIndex] ?? (CommitPayloads[MyIndex] = MakeSignedPayload(new Commit
- {
- Signature = EnsureHeader().Sign(keyPair)
- }));
- }
-
- private ExtensiblePayload MakeSignedPayload(ConsensusMessage message)
- {
- message.BlockIndex = Block.Index;
- message.ValidatorIndex = (byte)MyIndex;
- message.ViewNumber = ViewNumber;
- ExtensiblePayload payload = CreatePayload(message, null);
- SignPayload(payload);
- return payload;
- }
-
- private void SignPayload(ExtensiblePayload payload)
- {
- ContractParametersContext sc;
- try
- {
- sc = new ContractParametersContext(payload);
- wallet.Sign(sc);
- }
- catch (InvalidOperationException)
- {
- return;
- }
- payload.Witness = sc.GetWitnesses()[0];
- }
-
- ///
- /// Return the expected block size
- ///
- internal int GetExpectedBlockSize()
- {
- return GetExpectedBlockSizeWithoutTransactions(Transactions.Count) + // Base size
- Transactions.Values.Sum(u => u.Size); // Sum Txs
- }
-
- ///
- /// Return the expected block system fee
- ///
- internal long GetExpectedBlockSystemFee()
- {
- return Transactions.Values.Sum(u => u.SystemFee); // Sum Txs
- }
-
- ///
- /// Return the expected block size without txs
- ///
- /// Expected transactions
- internal int GetExpectedBlockSizeWithoutTransactions(int expectedTransactions)
- {
- var blockSize =
- // BlockBase
- sizeof(uint) + //Version
- UInt256.Length + //PrevHash
- UInt256.Length + //MerkleRoot
- sizeof(ulong) + //Timestamp
- sizeof(uint) + //Index
- UInt160.Length + //NextConsensus
- 1 + //
- _witnessSize; //Witness
-
- blockSize +=
- // Block
- Block.ConsensusData.Size + //ConsensusData
- IO.Helper.GetVarSize(expectedTransactions + 1); //Transactions count
-
- return blockSize;
- }
-
- ///
- /// Prevent that block exceed the max size
- ///
- /// Ordered transactions
- internal void EnsureMaxBlockLimitation(IEnumerable txs)
- {
- uint maxBlockSize = NativeContract.Policy.GetMaxBlockSize(Snapshot);
- long maxBlockSystemFee = NativeContract.Policy.GetMaxBlockSystemFee(Snapshot);
- uint maxTransactionsPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(Snapshot);
-
- // Limit Speaker proposal to the limit `MaxTransactionsPerBlock` or all available transactions of the mempool
- txs = txs.Take((int)maxTransactionsPerBlock);
- List hashes = new List();
- Transactions = new Dictionary();
- VerificationContext = new TransactionVerificationContext();
-
- // Expected block size
- var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count());
- var blockSystemFee = 0L;
-
- // Iterate transaction until reach the size or maximum system fee
- foreach (Transaction tx in txs)
- {
- // Check if maximum block size has been already exceeded with the current selected set
- blockSize += tx.Size;
- if (blockSize > maxBlockSize) break;
-
- // Check if maximum block system fee has been already exceeded with the current selected set
- blockSystemFee += tx.SystemFee;
- if (blockSystemFee > maxBlockSystemFee) break;
-
- hashes.Add(tx.Hash);
- Transactions.Add(tx.Hash, tx);
- VerificationContext.AddTransaction(tx);
- }
-
- TransactionHashes = hashes.ToArray();
- }
-
- public ExtensiblePayload MakePrepareRequest()
- {
- var random = new Random();
- Span buffer = stackalloc byte[sizeof(ulong)];
- random.NextBytes(buffer);
- Block.ConsensusData.Nonce = BitConverter.ToUInt64(buffer);
- EnsureMaxBlockLimitation(Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions());
- Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1);
-
- return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareRequest
- {
- Version = Block.Version,
- PrevHash = Block.PrevHash,
- Timestamp = Block.Timestamp,
- Nonce = Block.ConsensusData.Nonce,
- TransactionHashes = TransactionHashes
- });
- }
-
- public ExtensiblePayload MakeRecoveryRequest()
- {
- return MakeSignedPayload(new RecoveryRequest
- {
- Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS()
- });
- }
-
- 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 => 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 => 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 => GetCommitPayloadCompact(p)).ToDictionary(p => (int)p.ValidatorIndex)
- : new Dictionary()
- });
- }
-
- public ExtensiblePayload MakePrepareResponse()
- {
- return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareResponse
- {
- PreparationHash = PreparationPayloads[Block.ConsensusData.PrimaryIndex].Hash
- });
- }
-
- public void Reset(byte viewNumber)
- {
- if (viewNumber == 0)
- {
- Snapshot?.Dispose();
- Snapshot = Blockchain.Singleton.GetSnapshot();
- Block = new Block
- {
- PrevHash = Snapshot.CurrentBlockHash,
- Index = Snapshot.Height + 1,
- NextConsensus = Blockchain.GetConsensusAddress(
- NativeContract.NEO.ShouldRefreshCommittee(Snapshot.Height + 1) ?
- NativeContract.NEO.ComputeNextBlockValidators(Snapshot) :
- NativeContract.NEO.GetNextBlockValidators(Snapshot))
- };
- var pv = Validators;
- Validators = NativeContract.NEO.GetNextBlockValidators(Snapshot);
- if (_witnessSize == 0 || (pv != null && pv.Length != Validators.Length))
- {
- // Compute the expected size of the witness
- using (ScriptBuilder sb = new ScriptBuilder())
- {
- for (int x = 0; x < M; x++)
- {
- sb.EmitPush(new byte[64]);
- }
- _witnessSize = new Witness
- {
- InvocationScript = sb.ToArray(),
- VerificationScript = Contract.CreateMultiSigRedeemScript(M, Validators)
- }.Size;
- }
- }
- MyIndex = -1;
- ChangeViewPayloads = new ExtensiblePayload[Validators.Length];
- LastChangeViewPayloads = new ExtensiblePayload[Validators.Length];
- CommitPayloads = new ExtensiblePayload[Validators.Length];
- if (ValidatorsChanged || LastSeenMessage is null)
- {
- var previous_last_seen_message = LastSeenMessage;
- LastSeenMessage = new Dictionary();
- foreach (var validator in Validators)
- {
- if (previous_last_seen_message != null && previous_last_seen_message.TryGetValue(validator, out var value))
- LastSeenMessage[validator] = value;
- else
- LastSeenMessage[validator] = Snapshot.Height;
- }
- }
- keyPair = null;
- for (int i = 0; i < Validators.Length; i++)
- {
- WalletAccount account = wallet?.GetAccount(Validators[i]);
- if (account?.HasKey != true) continue;
- MyIndex = i;
- keyPair = account.GetKey();
- break;
- }
- cachedMessages = new Dictionary();
- }
- else
- {
- for (int i = 0; i < LastChangeViewPayloads.Length; i++)
- if (GetMessage(ChangeViewPayloads[i])?.NewViewNumber >= viewNumber)
- LastChangeViewPayloads[i] = ChangeViewPayloads[i];
- else
- LastChangeViewPayloads[i] = null;
- }
- ViewNumber = viewNumber;
- Block.ConsensusData = new ConsensusData
- {
- PrimaryIndex = GetPrimaryIndex(viewNumber)
- };
- Block.MerkleRoot = null;
- Block.Timestamp = 0;
- Block.Transactions = null;
- TransactionHashes = null;
- PreparationPayloads = new ExtensiblePayload[Validators.Length];
- if (MyIndex >= 0) LastSeenMessage[Validators[MyIndex]] = Block.Index;
- }
-
- public void Save()
- {
- store.PutSync(ConsensusStatePrefix, null, this.ToArray());
- }
-
- public void Serialize(BinaryWriter writer)
- {
- writer.Write(Block.Version);
- writer.Write(Block.Index);
- writer.Write(Block.Timestamp);
- writer.Write(Block.NextConsensus ?? UInt160.Zero);
- writer.Write(Block.ConsensusData);
- writer.Write(ViewNumber);
- writer.Write(TransactionHashes ?? new UInt256[0]);
- writer.Write(Transactions?.Values.ToArray() ?? new Transaction[0]);
- writer.WriteNullableArray(PreparationPayloads);
- writer.WriteNullableArray(CommitPayloads);
- writer.WriteNullableArray(ChangeViewPayloads);
- writer.WriteNullableArray(LastChangeViewPayloads);
- }
- }
-}
diff --git a/src/neo/Consensus/ConsensusMessage.cs b/src/neo/Consensus/ConsensusMessage.cs
deleted file mode 100644
index fceae7073a..0000000000
--- a/src/neo/Consensus/ConsensusMessage.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using Neo.IO;
-using Neo.IO.Caching;
-using System;
-using System.IO;
-
-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) + //Type
- sizeof(uint) + //BlockIndex
- sizeof(byte) + //ValidatorIndex
- sizeof(byte); //ViewNumber
-
- protected ConsensusMessage(ConsensusMessageType type)
- {
- if (!Enum.IsDefined(typeof(ConsensusMessageType), type))
- throw new ArgumentOutOfRangeException(nameof(type));
- this.Type = type;
- }
-
- 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();
- }
-
- public static ConsensusMessage DeserializeFrom(byte[] data)
- {
- ConsensusMessageType type = (ConsensusMessageType)data[0];
- ISerializable message = ReflectionCache.CreateSerializable(type, data);
- if (message is null) throw new FormatException();
- return (ConsensusMessage)message;
- }
-
- 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/ConsensusMessageType.cs b/src/neo/Consensus/ConsensusMessageType.cs
deleted file mode 100644
index fe13207db1..0000000000
--- a/src/neo/Consensus/ConsensusMessageType.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Neo.IO.Caching;
-
-namespace Neo.Consensus
-{
- public enum ConsensusMessageType : byte
- {
- [ReflectionCache(typeof(ChangeView))]
- ChangeView = 0x00,
-
- [ReflectionCache(typeof(PrepareRequest))]
- PrepareRequest = 0x20,
- [ReflectionCache(typeof(PrepareResponse))]
- PrepareResponse = 0x21,
- [ReflectionCache(typeof(Commit))]
- Commit = 0x30,
-
- [ReflectionCache(typeof(RecoveryRequest))]
- RecoveryRequest = 0x40,
- [ReflectionCache(typeof(RecoveryMessage))]
- RecoveryMessage = 0x41,
- }
-}
diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs
deleted file mode 100644
index 3361315e06..0000000000
--- a/src/neo/Consensus/ConsensusService.cs
+++ /dev/null
@@ -1,694 +0,0 @@
-using Akka.Actor;
-using Akka.Configuration;
-using Neo.Cryptography;
-using Neo.IO;
-using Neo.IO.Actors;
-using Neo.Ledger;
-using Neo.Network.P2P;
-using Neo.Network.P2P.Payloads;
-using Neo.Persistence;
-using Neo.SmartContract;
-using Neo.SmartContract.Native;
-using Neo.Wallets;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace Neo.Consensus
-{
- public sealed class ConsensusService : UntypedActor
- {
- public class Start { public bool IgnoreRecoveryLogs; }
- public class SetViewNumber { public byte ViewNumber; }
- internal class Timer { public uint Height; public byte ViewNumber; }
-
- private readonly ConsensusContext context;
- private readonly IActorRef localNode;
- private readonly IActorRef taskManager;
- private readonly IActorRef blockchain;
- private ICancelable timer_token;
- private DateTime block_received_time;
- private uint block_received_index;
- private bool started = false;
-
- ///
- /// This will record the information from last scheduled timer
- ///
- private DateTime clock_started = TimeProvider.Current.UtcNow;
- private TimeSpan expected_delay = TimeSpan.Zero;
-
- ///
- /// This will be cleared every block (so it will not grow out of control, but is used to prevent repeatedly
- /// responding to the same message.
- ///
- private readonly HashSet knownHashes = new HashSet();
- ///
- /// This variable is only true during OnRecoveryMessageReceived
- ///
- private bool isRecovering = false;
-
- public ConsensusService(IActorRef localNode, IActorRef taskManager, IActorRef blockchain, IStore store, Wallet wallet)
- : this(localNode, taskManager, blockchain, new ConsensusContext(wallet, store))
- {
- }
-
- internal ConsensusService(IActorRef localNode, IActorRef taskManager, IActorRef blockchain, ConsensusContext context)
- {
- this.localNode = localNode;
- this.taskManager = taskManager;
- this.blockchain = blockchain;
- this.context = context;
- Context.System.EventStream.Subscribe(Self, typeof(Blockchain.PersistCompleted));
- Context.System.EventStream.Subscribe(Self, typeof(Blockchain.RelayResult));
- }
-
- private bool AddTransaction(Transaction tx, bool verify)
- {
- if (verify)
- {
- VerifyResult result = tx.Verify(context.Snapshot, context.VerificationContext);
- if (result == VerifyResult.PolicyFail)
- {
- Log($"reject tx: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
- RequestChangeView(ChangeViewReason.TxRejectedByPolicy);
- return false;
- }
- else if (result != VerifyResult.Succeed)
- {
- Log($"Invalid transaction: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning);
- RequestChangeView(ChangeViewReason.TxInvalid);
- return false;
- }
- }
- context.Transactions[tx.Hash] = tx;
- context.VerificationContext.AddTransaction(tx);
- return CheckPrepareResponse();
- }
-
- private bool CheckPrepareResponse()
- {
- if (context.TransactionHashes.Length == context.Transactions.Count)
- {
- // if we are the primary for this view, but acting as a backup because we recovered our own
- // previously sent prepare request, then we don't want to send a prepare response.
- if (context.IsPrimary || context.WatchOnly) return true;
-
- // Check maximum block size via Native Contract policy
- if (context.GetExpectedBlockSize() > NativeContract.Policy.GetMaxBlockSize(context.Snapshot))
- {
- Log($"rejected block: {context.Block.Index} The size exceed the policy", LogLevel.Warning);
- RequestChangeView(ChangeViewReason.BlockRejectedByPolicy);
- return false;
- }
- // Check maximum block system fee via Native Contract policy
- if (context.GetExpectedBlockSystemFee() > NativeContract.Policy.GetMaxBlockSystemFee(context.Snapshot))
- {
- Log($"rejected block: {context.Block.Index} The system fee exceed the policy", LogLevel.Warning);
- RequestChangeView(ChangeViewReason.BlockRejectedByPolicy);
- return false;
- }
-
- // Timeout extension due to prepare response sent
- // around 2*15/M=30.0/5 ~ 40% block time (for M=5)
- ExtendTimerByFactor(2);
-
- Log($"send prepare response");
- localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareResponse() });
- CheckPreparations();
- }
- return true;
- }
-
- private void ChangeTimer(TimeSpan delay)
- {
- clock_started = TimeProvider.Current.UtcNow;
- expected_delay = delay;
- timer_token.CancelIfNotNull();
- timer_token = Context.System.Scheduler.ScheduleTellOnceCancelable(delay, Self, new Timer
- {
- Height = context.Block.Index,
- ViewNumber = context.ViewNumber
- }, ActorRefs.NoSender);
- }
-
- private void CheckCommits()
- {
- 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;
- Block block = context.CreateBlock();
- Log($"relay block: height={block.Index} hash={block.Hash} tx={block.Transactions.Length}");
- blockchain.Tell(block);
- }
- }
-
- 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 (messages.Count(p => p != null && p.NewViewNumber >= viewNumber) >= context.M)
- {
- if (!context.WatchOnly)
- {
- 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)
- localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeChangeView(ChangeViewReason.ChangeAgreement) });
- }
- InitializeConsensus(viewNumber);
- }
- }
-
- private void CheckPreparations()
- {
- if (context.PreparationPayloads.Count(p => p != null) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p)))
- {
- ExtensiblePayload payload = context.MakeCommit();
- Log($"send commit");
- context.Save();
- localNode.Tell(new LocalNode.SendDirectly { Inventory = payload });
- // Set timer, so we will resend the commit in case of a networking issue
- ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock));
- CheckCommits();
- }
- }
-
- private void InitializeConsensus(byte viewNumber)
- {
- context.Reset(viewNumber);
- if (viewNumber > 0)
- Log($"changeview: view={viewNumber} primary={context.Validators[context.GetPrimaryIndex((byte)(viewNumber - 1u))]}", LogLevel.Warning);
- Log($"initialize: height={context.Block.Index} view={viewNumber} index={context.MyIndex} role={(context.IsPrimary ? "Primary" : context.WatchOnly ? "WatchOnly" : "Backup")}");
- if (context.WatchOnly) return;
- if (context.IsPrimary)
- {
- if (isRecovering)
- {
- ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (viewNumber + 1)));
- }
- else
- {
- TimeSpan span = Blockchain.TimePerBlock;
- if (block_received_index + 1 == context.Block.Index)
- {
- var diff = TimeProvider.Current.UtcNow - block_received_time;
- if (diff >= span)
- span = TimeSpan.Zero;
- else
- span -= diff;
- }
- ChangeTimer(span);
- }
- }
- else
- {
- ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (viewNumber + 1)));
- }
- }
-
- private void Log(string message, LogLevel level = LogLevel.Info)
- {
- Utility.Log(nameof(ConsensusService), level, message);
- }
-
- private void OnChangeViewReceived(ExtensiblePayload payload, ChangeView message)
- {
- if (message.NewViewNumber <= context.ViewNumber)
- OnRecoveryRequestReceived(payload, message);
-
- if (context.CommitSent) return;
-
- var expectedView = context.GetMessage(context.ChangeViewPayloads[message.ValidatorIndex])?.NewViewNumber ?? 0;
- if (message.NewViewNumber <= expectedView)
- return;
-
- 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(ExtensiblePayload payload, Commit commit)
- {
- ref ExtensiblePayload existingCommitPayload = ref context.CommitPayloads[commit.ValidatorIndex];
- if (existingCommitPayload != null)
- {
- if (existingCommitPayload.Hash != payload.Hash)
- Log($"{nameof(OnCommitReceived)}: different commit from validator! height={commit.BlockIndex} index={commit.ValidatorIndex} view={commit.ViewNumber} existingView={context.GetMessage(existingCommitPayload).ViewNumber}", LogLevel.Warning);
- return;
- }
-
- // Timeout extension: commit has been received with success
- // around 4*15s/M=60.0s/5=12.0s ~ 80% block time (for M=5)
- ExtendTimerByFactor(4);
-
- if (commit.ViewNumber == context.ViewNumber)
- {
- 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[commit.ValidatorIndex]))
- {
- existingCommitPayload = payload;
- CheckCommits();
- }
- return;
- }
- // Receiving commit from another view
- Log($"{nameof(OnCommitReceived)}: record commit for different view={commit.ViewNumber} index={commit.ValidatorIndex} height={commit.BlockIndex}");
- existingCommitPayload = payload;
- }
-
- // this function increases existing timer (never decreases) with a value proportional to `maxDelayInBlockTimes`*`Blockchain.MillisecondsPerBlock`
- private void ExtendTimerByFactor(int maxDelayInBlockTimes)
- {
- TimeSpan nextDelay = expected_delay - (TimeProvider.Current.UtcNow - clock_started) + TimeSpan.FromMilliseconds(maxDelayInBlockTimes * Blockchain.MillisecondsPerBlock / (double)context.M);
- if (!context.WatchOnly && !context.ViewChanging && !context.CommitSent && (nextDelay > TimeSpan.Zero))
- ChangeTimer(nextDelay);
- }
-
- private void OnConsensusPayload(ExtensiblePayload payload)
- {
- if (context.BlockSent) return;
- ConsensusMessage message;
- try
- {
- message = context.GetMessage(payload);
- }
- catch (FormatException)
- {
- return;
- }
- catch (IOException)
- {
- 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:
- OnChangeViewReceived(payload, view);
- break;
- case PrepareRequest request:
- OnPrepareRequestReceived(payload, request);
- break;
- case PrepareResponse response:
- OnPrepareResponseReceived(payload, response);
- break;
- case Commit commit:
- OnCommitReceived(payload, commit);
- break;
- case RecoveryRequest request:
- OnRecoveryRequestReceived(payload, request);
- break;
- case RecoveryMessage recovery:
- OnRecoveryMessageReceived(recovery);
- break;
- }
- }
-
- private void OnPersistCompleted(Block block)
- {
- Log($"persist block: height={block.Index} hash={block.Hash} tx={block.Transactions.Length}");
- knownHashes.Clear();
- InitializeConsensus(0);
- }
-
- 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={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex}");
- try
- {
- if (message.ViewNumber > context.ViewNumber)
- {
- if (context.CommitSent) return;
- ExtensiblePayload[] changeViewPayloads = message.GetChangeViewPayloads(context);
- totalChangeViews = changeViewPayloads.Length;
- foreach (ExtensiblePayload changeViewPayload in changeViewPayloads)
- if (ReverifyAndProcessPayload(changeViewPayload)) validChangeViews++;
- }
- if (message.ViewNumber == context.ViewNumber && !context.NotAcceptingPayloadsDueToViewChanging && !context.CommitSent)
- {
- if (!context.RequestSentOrReceived)
- {
- ExtensiblePayload prepareRequestPayload = message.GetPrepareRequestPayload(context);
- if (prepareRequestPayload != null)
- {
- totalPrepReq = 1;
- if (ReverifyAndProcessPayload(prepareRequestPayload)) validPrepReq++;
- }
- else if (context.IsPrimary)
- SendPrepareRequest();
- }
- ExtensiblePayload[] prepareResponsePayloads = message.GetPrepareResponsePayloads(context);
- totalPrepResponses = prepareResponsePayloads.Length;
- foreach (ExtensiblePayload prepareResponsePayload in prepareResponsePayloads)
- if (ReverifyAndProcessPayload(prepareResponsePayload)) validPrepResponses++;
- }
- if (message.ViewNumber <= context.ViewNumber)
- {
- // Ensure we know about all commits from lower view numbers.
- ExtensiblePayload[] commitPayloads = message.GetCommitPayloadsFromRecoveryMessage(context);
- totalCommits = commitPayloads.Length;
- foreach (ExtensiblePayload commitPayload in commitPayloads)
- if (ReverifyAndProcessPayload(commitPayload)) validCommits++;
- }
- }
- finally
- {
- Log($"{nameof(OnRecoveryMessageReceived)}: finished (valid/total) " +
- $"ChgView: {validChangeViews}/{totalChangeViews} " +
- $"PrepReq: {validPrepReq}/{totalPrepReq} " +
- $"PrepResp: {validPrepResponses}/{totalPrepResponses} " +
- $"Commits: {validCommits}/{totalCommits}");
- isRecovering = false;
- }
- }
-
- 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.
- // ChangeView messages include a Timestamp when the change view is sent, thus if a node restarts
- // and issues a change view for the same view, it will have a different hash and will correctly respond
- // again; however replay attacks of the ChangeView message from arbitrary nodes will not trigger an
- // additional recovery message response.
- if (!knownHashes.Add(payload.Hash)) return;
-
- Log($"On{message.GetType().Name}Received: height={message.BlockIndex} index={message.ValidatorIndex} view={message.ViewNumber}");
- if (context.WatchOnly) return;
- if (!context.CommitSent)
- {
- bool shouldSendRecovery = false;
- int allowedRecoveryNodeCount = context.F;
- // Limit recoveries to be sent from an upper limit of `f` nodes
- for (int i = 1; i <= allowedRecoveryNodeCount; i++)
- {
- var chosenIndex = (message.ValidatorIndex + i) % context.Validators.Length;
- if (chosenIndex != context.MyIndex) continue;
- shouldSendRecovery = true;
- break;
- }
-
- if (!shouldSendRecovery) return;
- }
- Log($"send recovery: view={context.ViewNumber}");
- localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryMessage() });
- }
-
- private void OnPrepareRequestReceived(ExtensiblePayload payload, PrepareRequest message)
- {
- if (context.RequestSentOrReceived || context.NotAcceptingPayloadsDueToViewChanging) return;
- 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);
- return;
- }
- if (message.TransactionHashes.Any(p => context.Snapshot.ContainsTransaction(p)))
- {
- Log($"Invalid request: transaction already exists", LogLevel.Warning);
- return;
- }
-
- // Timeout extension: prepare request has been received with success
- // around 2*15/M=30.0/5 ~ 40% block time (for M=5)
- ExtendTimerByFactor(2);
-
- context.Block.Timestamp = message.Timestamp;
- context.Block.ConsensusData.Nonce = message.Nonce;
- context.TransactionHashes = message.TransactionHashes;
- context.Transactions = new Dictionary();
- context.VerificationContext = new TransactionVerificationContext();
- for (int i = 0; i < context.PreparationPayloads.Length; i++)
- if (context.PreparationPayloads[i] != null)
- if (!context.GetMessage(context.PreparationPayloads[i]).PreparationHash.Equals(payload.Hash))
- context.PreparationPayloads[i] = null;
- context.PreparationPayloads[message.ValidatorIndex] = payload;
- byte[] hashData = context.EnsureHeader().GetHashData();
- for (int i = 0; i < context.CommitPayloads.Length; 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)
- {
- // There are no tx so we should act like if all the transactions were filled
- CheckPrepareResponse();
- return;
- }
-
- Dictionary mempoolVerified = Blockchain.Singleton.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash);
- List unverified = new List();
- foreach (UInt256 hash in context.TransactionHashes)
- {
- if (mempoolVerified.TryGetValue(hash, out Transaction tx))
- {
- if (!AddTransaction(tx, false))
- return;
- }
- else
- {
- if (Blockchain.Singleton.MemPool.TryGetValue(hash, out tx))
- unverified.Add(tx);
- }
- }
- foreach (Transaction tx in unverified)
- if (!AddTransaction(tx, true))
- return;
- if (context.Transactions.Count < context.TransactionHashes.Length)
- {
- UInt256[] hashes = context.TransactionHashes.Where(i => !context.Transactions.ContainsKey(i)).ToArray();
- taskManager.Tell(new TaskManager.RestartTasks
- {
- Payload = InvPayload.Create(InventoryType.TX, hashes)
- });
- }
- }
-
- private void OnPrepareResponseReceived(ExtensiblePayload payload, PrepareResponse message)
- {
- if (message.ViewNumber != context.ViewNumber) 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;
-
- // Timeout extension: prepare response has been received with success
- // around 2*15/M=30.0/5 ~ 40% block time (for M=5)
- ExtendTimerByFactor(2);
-
- 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();
- }
-
- protected override void OnReceive(object message)
- {
- if (message is Start options)
- {
- if (started) return;
- OnStart(options);
- }
- else
- {
- if (!started) return;
- switch (message)
- {
- case SetViewNumber setView:
- InitializeConsensus(setView.ViewNumber);
- break;
- case Timer timer:
- OnTimer(timer);
- break;
- case Transaction transaction:
- OnTransaction(transaction);
- break;
- case Blockchain.PersistCompleted completed:
- OnPersistCompleted(completed.Block);
- break;
- case Blockchain.RelayResult rr:
- if (rr.Result == VerifyResult.Succeed && rr.Inventory is ExtensiblePayload payload && payload.Category == "Consensus")
- OnConsensusPayload(payload);
- break;
- }
- }
- }
-
- private void RequestRecovery()
- {
- if (context.Block.Index == Blockchain.Singleton.HeaderHeight + 1)
- localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryRequest() });
- }
-
- private void OnStart(Start options)
- {
- Log("OnStart");
- started = true;
- if (!options.IgnoreRecoveryLogs && context.Load())
- {
- if (context.Transactions != null)
- {
- Sender.Ask(new Blockchain.FillMemoryPool
- {
- Transactions = context.Transactions.Values
- }).Wait();
- }
- if (context.CommitSent)
- {
- CheckPreparations();
- return;
- }
- }
- InitializeConsensus(0);
- // Issue a ChangeView with NewViewNumber of 0 to request recovery messages on start-up.
- if (!context.WatchOnly)
- RequestRecovery();
- }
-
- private void OnTimer(Timer timer)
- {
- if (context.WatchOnly || context.BlockSent) return;
- if (timer.Height != context.Block.Index || timer.ViewNumber != context.ViewNumber) return;
- Log($"timeout: height={timer.Height} view={timer.ViewNumber}");
- if (context.IsPrimary && !context.RequestSentOrReceived)
- {
- SendPrepareRequest();
- }
- else if ((context.IsPrimary && context.RequestSentOrReceived) || context.IsBackup)
- {
- if (context.CommitSent)
- {
- // Re-send commit periodically by sending recover message in case of a network issue.
- Log($"send recovery to resend commit");
- localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryMessage() });
- ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << 1));
- }
- else
- {
- var reason = ChangeViewReason.Timeout;
-
- if (context.Block != null && context.TransactionHashes?.Length > context.Transactions?.Count)
- {
- reason = ChangeViewReason.TxNotFound;
- }
-
- RequestChangeView(reason);
- }
- }
- }
-
- private void OnTransaction(Transaction transaction)
- {
- if (!context.IsBackup || context.NotAcceptingPayloadsDueToViewChanging || !context.RequestSentOrReceived || context.ResponseSent || context.BlockSent)
- return;
- if (context.Transactions.ContainsKey(transaction.Hash)) return;
- if (!context.TransactionHashes.Contains(transaction.Hash)) return;
- AddTransaction(transaction, true);
- }
-
- protected override void PostStop()
- {
- Log("OnStop");
- started = false;
- Context.System.EventStream.Unsubscribe(Self);
- context.Dispose();
- base.PostStop();
- }
-
- public static Props Props(IActorRef localNode, IActorRef taskManager, IActorRef blockchain, IStore store, Wallet wallet)
- {
- return Akka.Actor.Props.Create(() => new ConsensusService(localNode, taskManager, blockchain, store, wallet)).WithMailbox("consensus-service-mailbox");
- }
-
- private void RequestChangeView(ChangeViewReason reason)
- {
- if (context.WatchOnly) return;
- // Request for next view is always one view more than the current context.ViewNumber
- // Nodes will not contribute for changing to a view higher than (context.ViewNumber+1), unless they are recovered
- // The latter may happen by nodes in higher views with, at least, `M` proofs
- byte expectedView = context.ViewNumber;
- expectedView++;
- ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (expectedView + 1)));
- if ((context.CountCommitted + context.CountFailed) > context.F)
- {
- Log($"skip requesting change view: height={context.Block.Index} view={context.ViewNumber} nv={expectedView} nc={context.CountCommitted} nf={context.CountFailed} reason={reason}");
- RequestRecovery();
- return;
- }
- Log($"request change view: height={context.Block.Index} view={context.ViewNumber} nv={expectedView} nc={context.CountCommitted} nf={context.CountFailed} reason={reason}");
- localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeChangeView(reason) });
- CheckExpectedView(expectedView);
- }
-
- private bool ReverifyAndProcessPayload(ExtensiblePayload payload)
- {
- if (!payload.Verify(context.Snapshot)) return false;
- OnConsensusPayload(payload);
- return true;
- }
-
- private void SendPrepareRequest()
- {
- Log($"send prepare request: height={context.Block.Index} view={context.ViewNumber}");
- localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareRequest() });
-
- if (context.Validators.Length == 1)
- CheckPreparations();
-
- if (context.TransactionHashes.Length > 0)
- {
- foreach (InvPayload payload in InvPayload.CreateGroup(InventoryType.TX, context.TransactionHashes))
- localNode.Tell(Message.Create(MessageCommand.Inv, payload));
- }
- ChangeTimer(TimeSpan.FromMilliseconds((Blockchain.MillisecondsPerBlock << (context.ViewNumber + 1)) - (context.ViewNumber == 0 ? Blockchain.MillisecondsPerBlock : 0)));
- }
- }
-
- internal class ConsensusServiceMailbox : PriorityMailbox
- {
- public ConsensusServiceMailbox(Akka.Actor.Settings settings, Config config)
- : base(settings, config)
- {
- }
-
- internal protected override bool IsHighPriority(object message)
- {
- switch (message)
- {
- case ExtensiblePayload _:
- case ConsensusService.SetViewNumber _:
- case ConsensusService.Timer _:
- case Blockchain.PersistCompleted _:
- return true;
- default:
- return false;
- }
- }
- }
-}
diff --git a/src/neo/Consensus/PrepareRequest.cs b/src/neo/Consensus/PrepareRequest.cs
deleted file mode 100644
index 9e96ca4202..0000000000
--- a/src/neo/Consensus/PrepareRequest.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using Neo.IO;
-using Neo.Network.P2P.Payloads;
-using System;
-using System.IO;
-using System.Linq;
-
-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(uint) //Version
- + UInt256.Length //PrevHash
- + sizeof(ulong) //Timestamp
- + sizeof(ulong) //Nonce
- + TransactionHashes.GetVarSize(); //TransactionHashes
-
- public PrepareRequest()
- : base(ConsensusMessageType.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);
- if (TransactionHashes.Distinct().Count() != TransactionHashes.Length)
- throw new FormatException();
- }
-
- 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/PrepareResponse.cs b/src/neo/Consensus/PrepareResponse.cs
deleted file mode 100644
index 7c2956ccc2..0000000000
--- a/src/neo/Consensus/PrepareResponse.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Neo.IO;
-using System.IO;
-
-namespace Neo.Consensus
-{
- public class PrepareResponse : ConsensusMessage
- {
- public UInt256 PreparationHash;
-
- public override int Size => base.Size + PreparationHash.Size;
-
- public PrepareResponse()
- : base(ConsensusMessageType.PrepareResponse)
- {
- }
-
- public override void Deserialize(BinaryReader reader)
- {
- base.Deserialize(reader);
- PreparationHash = reader.ReadSerializable();
- }
-
- public override void Serialize(BinaryWriter writer)
- {
- base.Serialize(writer);
- writer.Write(PreparationHash);
- }
- }
-}
diff --git a/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs
deleted file mode 100644
index 8419193de6..0000000000
--- a/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Neo.IO;
-using System;
-using System.IO;
-
-namespace Neo.Consensus
-{
- partial class RecoveryMessage
- {
- public class ChangeViewPayloadCompact : ISerializable
- {
- public byte ValidatorIndex;
- public byte OriginalViewNumber;
- public ulong Timestamp;
- public byte[] InvocationScript;
-
- int ISerializable.Size =>
- sizeof(byte) + //ValidatorIndex
- sizeof(byte) + //OriginalViewNumber
- sizeof(ulong) + //Timestamp
- InvocationScript.GetVarSize(); //InvocationScript
-
- void ISerializable.Deserialize(BinaryReader reader)
- {
- ValidatorIndex = reader.ReadByte();
- if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount)
- throw new FormatException();
- OriginalViewNumber = reader.ReadByte();
- Timestamp = reader.ReadUInt64();
- InvocationScript = reader.ReadVarBytes(1024);
- }
-
- void ISerializable.Serialize(BinaryWriter writer)
- {
- writer.Write(ValidatorIndex);
- writer.Write(OriginalViewNumber);
- writer.Write(Timestamp);
- writer.WriteVarBytes(InvocationScript);
- }
- }
- }
-}
diff --git a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs
deleted file mode 100644
index 81b3bc37af..0000000000
--- a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Neo.IO;
-using System;
-using System.IO;
-
-namespace Neo.Consensus
-{
- partial class RecoveryMessage
- {
- public class CommitPayloadCompact : ISerializable
- {
- public byte ViewNumber;
- public byte ValidatorIndex;
- public byte[] Signature;
- public byte[] InvocationScript;
-
- int ISerializable.Size =>
- sizeof(byte) + //ViewNumber
- sizeof(byte) + //ValidatorIndex
- Signature.Length + //Signature
- InvocationScript.GetVarSize(); //InvocationScript
-
- void ISerializable.Deserialize(BinaryReader reader)
- {
- ViewNumber = reader.ReadByte();
- ValidatorIndex = reader.ReadByte();
- if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount)
- throw new FormatException();
- Signature = reader.ReadFixedBytes(64);
- InvocationScript = reader.ReadVarBytes(1024);
- }
-
- void ISerializable.Serialize(BinaryWriter writer)
- {
- writer.Write(ViewNumber);
- writer.Write(ValidatorIndex);
- writer.Write(Signature);
- writer.WriteVarBytes(InvocationScript);
- }
- }
- }
-}
diff --git a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs
deleted file mode 100644
index 72fbd2de2f..0000000000
--- a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using Neo.IO;
-using System;
-using System.IO;
-
-namespace Neo.Consensus
-{
- partial class RecoveryMessage
- {
- public class PreparationPayloadCompact : ISerializable
- {
- public byte ValidatorIndex;
- public byte[] InvocationScript;
-
- int ISerializable.Size =>
- sizeof(byte) + //ValidatorIndex
- InvocationScript.GetVarSize(); //InvocationScript
-
- void ISerializable.Deserialize(BinaryReader reader)
- {
- ValidatorIndex = reader.ReadByte();
- if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount)
- throw new FormatException();
- InvocationScript = reader.ReadVarBytes(1024);
- }
-
- void ISerializable.Serialize(BinaryWriter writer)
- {
- writer.Write(ValidatorIndex);
- writer.WriteVarBytes(InvocationScript);
- }
- }
- }
-}
diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs
deleted file mode 100644
index 11b8bd0361..0000000000
--- a/src/neo/Consensus/RecoveryMessage.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-using Neo.IO;
-using Neo.Network.P2P.Payloads;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-
-namespace Neo.Consensus
-{
- public partial class RecoveryMessage : ConsensusMessage
- {
- public Dictionary ChangeViewMessages;
- public PrepareRequest PrepareRequestMessage;
- /// The PreparationHash in case the PrepareRequest hasn't been received yet.
- /// This can be null if the PrepareRequest information is present, since it can be derived in that case.
- public UInt256 PreparationHash;
- public Dictionary PreparationMessages;
- public Dictionary CommitMessages;
-
- public override int Size => base.Size
- + /* ChangeViewMessages */ ChangeViewMessages?.Values.GetVarSize() ?? 0
- + /* PrepareRequestMessage */ 1 + PrepareRequestMessage?.Size ?? 0
- + /* PreparationHash */ PreparationHash?.Size ?? 0
- + /* PreparationMessages */ PreparationMessages?.Values.GetVarSize() ?? 0
- + /* CommitMessages */ CommitMessages?.Values.GetVarSize() ?? 0;
-
- public RecoveryMessage() : base(ConsensusMessageType.RecoveryMessage)
- {
- }
-
- public override void Deserialize(BinaryReader reader)
- {
- base.Deserialize(reader);
- ChangeViewMessages = reader.ReadSerializableArray(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex);
- if (reader.ReadBoolean())
- PrepareRequestMessage = reader.ReadSerializable();
- else
- {
- int preparationHashSize = UInt256.Zero.Size;
- if (preparationHashSize == (int)reader.ReadVarInt((ulong)preparationHashSize))
- PreparationHash = new UInt256(reader.ReadFixedBytes(preparationHashSize));
- }
-
- PreparationMessages = reader.ReadSerializableArray(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex);
- CommitMessages = reader.ReadSerializableArray(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex);
- }
-
- internal ExtensiblePayload[] GetChangeViewPayloads(ConsensusContext context)
- {
- return ChangeViewMessages.Values.Select(p => context.CreatePayload(new ChangeView
- {
- BlockIndex = BlockIndex,
- ValidatorIndex = p.ValidatorIndex,
- ViewNumber = p.OriginalViewNumber,
- Timestamp = p.Timestamp
- }, p.InvocationScript)).ToArray();
- }
-
- internal ExtensiblePayload[] GetCommitPayloadsFromRecoveryMessage(ConsensusContext context)
- {
- return CommitMessages.Values.Select(p => context.CreatePayload(new Commit
- {
- BlockIndex = BlockIndex,
- ValidatorIndex = p.ValidatorIndex,
- ViewNumber = p.ViewNumber,
- Signature = p.Signature
- }, p.InvocationScript)).ToArray();
- }
-
- internal ExtensiblePayload GetPrepareRequestPayload(ConsensusContext context)
- {
- if (PrepareRequestMessage == null) return null;
- if (!PreparationMessages.TryGetValue(context.Block.ConsensusData.PrimaryIndex, out PreparationPayloadCompact compact))
- return null;
- return context.CreatePayload(PrepareRequestMessage, compact.InvocationScript);
- }
-
- internal ExtensiblePayload[] GetPrepareResponsePayloads(ConsensusContext context)
- {
- UInt256 preparationHash = PreparationHash ?? context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex]?.Hash;
- if (preparationHash is null) return Array.Empty();
- return PreparationMessages.Values.Where(p => p.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex).Select(p => context.CreatePayload(new PrepareResponse
- {
- BlockIndex = BlockIndex,
- ValidatorIndex = p.ValidatorIndex,
- ViewNumber = ViewNumber,
- PreparationHash = preparationHash
- }, p.InvocationScript)).ToArray();
- }
-
- public override void Serialize(BinaryWriter writer)
- {
- base.Serialize(writer);
- writer.Write(ChangeViewMessages.Values.ToArray());
- bool hasPrepareRequestMessage = PrepareRequestMessage != null;
- writer.Write(hasPrepareRequestMessage);
- if (hasPrepareRequestMessage)
- writer.Write(PrepareRequestMessage);
- else
- {
- if (PreparationHash == null)
- writer.WriteVarInt(0);
- else
- writer.WriteVarBytes(PreparationHash.ToArray());
- }
-
- writer.Write(PreparationMessages.Values.ToArray());
- writer.Write(CommitMessages.Values.ToArray());
- }
- }
-}
diff --git a/src/neo/Consensus/RecoveryRequest.cs b/src/neo/Consensus/RecoveryRequest.cs
deleted file mode 100644
index 6ea0d7c2f7..0000000000
--- a/src/neo/Consensus/RecoveryRequest.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.IO;
-
-namespace Neo.Consensus
-{
- public class RecoveryRequest : ConsensusMessage
- {
- ///
- /// Timestamp of when the ChangeView message was created. This allows receiving nodes to ensure
- /// they only respond once to a specific RecoveryRequest request.
- /// In this sense, it prevents replay of the RecoveryRequest message from the repeatedly broadcast of Recovery's messages.
- ///
- public ulong Timestamp;
-
- public override int Size => base.Size
- + sizeof(ulong); //Timestamp
-
- public RecoveryRequest() : base(ConsensusMessageType.RecoveryRequest) { }
-
- public override void Deserialize(BinaryReader reader)
- {
- base.Deserialize(reader);
- Timestamp = reader.ReadUInt64();
- }
-
- public override void Serialize(BinaryWriter writer)
- {
- base.Serialize(writer);
- writer.Write(Timestamp);
- }
- }
-}
diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs
index 76980ca319..9ba6eb202d 100644
--- a/src/neo/NeoSystem.cs
+++ b/src/neo/NeoSystem.cs
@@ -1,10 +1,8 @@
using Akka.Actor;
-using Neo.Consensus;
using Neo.Ledger;
using Neo.Network.P2P;
using Neo.Persistence;
using Neo.Plugins;
-using Neo.Wallets;
using System;
namespace Neo
@@ -15,12 +13,10 @@ public class NeoSystem : IDisposable
$"akka {{ log-dead-letters = off , loglevel = warning, loggers = [ \"{typeof(Utility.Logger).AssemblyQualifiedName}\" ] }}" +
$"blockchain-mailbox {{ mailbox-type: \"{typeof(BlockchainMailbox).AssemblyQualifiedName}\" }}" +
$"task-manager-mailbox {{ mailbox-type: \"{typeof(TaskManagerMailbox).AssemblyQualifiedName}\" }}" +
- $"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}" +
- $"consensus-service-mailbox {{ mailbox-type: \"{typeof(ConsensusServiceMailbox).AssemblyQualifiedName}\" }}");
+ $"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}");
public IActorRef Blockchain { get; }
public IActorRef LocalNode { get; }
- internal IActorRef TaskManager { get; }
- public IActorRef Consensus { get; private set; }
+ public IActorRef TaskManager { get; }
private readonly string storage_engine;
private readonly IStore store;
@@ -86,12 +82,6 @@ internal void ResumeNodeStartup()
}
}
- public void StartConsensus(Wallet wallet, IStore consensus_store = null, bool ignoreRecoveryLogs = false)
- {
- Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, this.Blockchain, consensus_store ?? store, wallet));
- Consensus.Tell(new ConsensusService.Start { IgnoreRecoveryLogs = ignoreRecoveryLogs }, Blockchain);
- }
-
public void StartNode(ChannelsConfig config)
{
start_message = config;
diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs
index c8bc937001..c2941c38bd 100644
--- a/src/neo/Network/P2P/LocalNode.cs
+++ b/src/neo/Network/P2P/LocalNode.cs
@@ -15,8 +15,8 @@ namespace Neo.Network.P2P
{
public class LocalNode : Peer
{
- internal class RelayDirectly { public IInventory Inventory; }
- internal class SendDirectly { public IInventory Inventory; }
+ public class RelayDirectly { public IInventory Inventory; }
+ public class SendDirectly { public IInventory Inventory; }
public const uint ProtocolVersion = 0;
private const int MaxCountFromSeedList = 5;
diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs
index b0b1051c71..7d85875c56 100644
--- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs
+++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs
@@ -288,15 +288,10 @@ private void OnGetHeadersMessageReceived(GetBlockByIndexPayload payload)
private void OnInventoryReceived(IInventory inventory)
{
pendingKnownHashes.Remove(inventory.Hash);
- switch (inventory)
+ if (inventory is Block block)
{
- case Transaction transaction:
- system.Consensus?.Tell(transaction);
- break;
- case Block block:
- if (block.Index > Blockchain.Singleton.Height + InvPayload.MaxHashesCount) return;
- UpdateLastBlockIndex(block.Index, false);
- break;
+ if (block.Index > Blockchain.Singleton.Height + InvPayload.MaxHashesCount) return;
+ UpdateLastBlockIndex(block.Index, false);
}
knownHashes.Add(inventory.Hash);
system.TaskManager.Tell(inventory);
diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs
index 07088a9fb5..ef6e09e443 100644
--- a/src/neo/Network/P2P/TaskManager.cs
+++ b/src/neo/Network/P2P/TaskManager.cs
@@ -13,11 +13,11 @@
namespace Neo.Network.P2P
{
- internal class TaskManager : UntypedActor
+ public class TaskManager : UntypedActor
{
- public class Register { public VersionPayload Version; }
- public class Update { public uint LastBlockIndex; public bool RequestTasks; }
- public class NewTasks { public InvPayload Payload; }
+ internal class Register { public VersionPayload Version; }
+ internal class Update { public uint LastBlockIndex; public bool RequestTasks; }
+ internal class NewTasks { public InvPayload Payload; }
public class RestartTasks { public InvPayload Payload; }
private class Timer { }
diff --git a/src/neo/Persistence/Prefixes.cs b/src/neo/Persistence/Prefixes.cs
index fcd7549325..3b63665326 100644
--- a/src/neo/Persistence/Prefixes.cs
+++ b/src/neo/Persistence/Prefixes.cs
@@ -10,10 +10,5 @@ internal static class Prefixes
public const byte IX_HeaderHashList = 0x80;
public const byte IX_CurrentBlock = 0xc0;
public const byte IX_CurrentHeader = 0xc1;
-
- /* Prefixes 0xf0 to 0xff are reserved for external use.
- *
- * Note: The saved consensus state uses the Prefix 0xf4
- */
}
}
diff --git a/src/neo/Plugins/IConsensusProvider.cs b/src/neo/Plugins/IConsensusProvider.cs
new file mode 100644
index 0000000000..a7e021863a
--- /dev/null
+++ b/src/neo/Plugins/IConsensusProvider.cs
@@ -0,0 +1,9 @@
+using Neo.Wallets;
+
+namespace Neo.Plugins
+{
+ public interface IConsensusProvider
+ {
+ void Start(Wallet wallet);
+ }
+}
diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs
index 8323339d75..ea792e721c 100644
--- a/src/neo/Plugins/IP2PPlugin.cs
+++ b/src/neo/Plugins/IP2PPlugin.cs
@@ -6,6 +6,6 @@ namespace Neo.Plugins
public interface IP2PPlugin
{
bool OnP2PMessage(Message message) => true;
- void OnVerifiedInventory(IInventory inventory);
+ void OnVerifiedInventory(IInventory inventory) { }
}
}
diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs
index 375cb628ef..64971dc640 100644
--- a/src/neo/SmartContract/Native/NeoToken.cs
+++ b/src/neo/SmartContract/Native/NeoToken.cs
@@ -303,7 +303,7 @@ private CachedCommittee GetCommitteeFromCache(StoreView snapshot)
return snapshot.Storages[CreateStorageKey(Prefix_Committee)].GetInteroperable();
}
- internal ECPoint[] ComputeNextBlockValidators(StoreView snapshot)
+ public ECPoint[] ComputeNextBlockValidators(StoreView snapshot)
{
return ComputeCommitteeMembers(snapshot).Select(p => p.PublicKey).Take(ProtocolSettings.Default.ValidatorsCount).OrderBy(p => p).ToArray();
}
diff --git a/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs b/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs
deleted file mode 100644
index 757d041801..0000000000
--- a/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using FluentAssertions;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Neo.Consensus;
-using Neo.IO;
-
-namespace Neo.UnitTests.Consensus
-{
- [TestClass]
- public class UT_ChangeViewPayloadCompact
- {
- [TestMethod]
- public void Size_Get()
- {
- var test = new RecoveryMessage.ChangeViewPayloadCompact() { Timestamp = 1, ValidatorIndex = 1, InvocationScript = new byte[0], OriginalViewNumber = 1 };
- ((ISerializable)test).Size.Should().Be(11);
-
- test = new RecoveryMessage.ChangeViewPayloadCompact() { Timestamp = 1, ValidatorIndex = 1, InvocationScript = new byte[1024], OriginalViewNumber = 1 };
- ((ISerializable)test).Size.Should().Be(1037);
- }
-
- [TestMethod]
- public void DeserializeAndSerialize()
- {
- var test = new RecoveryMessage.ChangeViewPayloadCompact() { Timestamp = 1, ValidatorIndex = 2, InvocationScript = new byte[] { 1, 2, 3 }, OriginalViewNumber = 3 };
- var clone = test.ToArray().AsSerializable();
-
- 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
deleted file mode 100644
index a900f90797..0000000000
--- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs
+++ /dev/null
@@ -1,994 +0,0 @@
-using Akka.Actor;
-using Akka.TestKit;
-using Akka.TestKit.Xunit2;
-using FluentAssertions;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Neo.Consensus;
-using Neo.Cryptography;
-using Neo.IO;
-using Neo.Ledger;
-using Neo.Network.P2P;
-using Neo.Network.P2P.Payloads;
-using Neo.SmartContract;
-using Neo.SmartContract.Native;
-using Neo.UnitTests.Cryptography;
-using Neo.Wallets;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Numerics;
-using System.Reflection;
-using System.Security.Cryptography;
-using static Neo.SmartContract.Native.NeoToken;
-using ECPoint = Neo.Cryptography.ECC.ECPoint;
-
-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();
- }
-
- [TestMethod]
- public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Commits()
- {
- var mockWallet = new Mock();
- mockWallet.Setup(p => p.GetAccount(It.IsAny())).Returns(p => new TestWalletAccount(p));
- Console.WriteLine($"\n(UT-Consensus) Wallet is: {mockWallet.Object.GetAccount(UInt160.Zero).GetKey().PublicKey}");
-
- var mockContext = new Mock(mockWallet.Object, Blockchain.Singleton.Store);
- 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
- new DateTime(1980, 05, 01, 0, 0, 5, 001, DateTimeKind.Utc), // For Initialize
- 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]} ");
- 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
- // ============================================================================
- // creating ConsensusService actor
- // ============================================================================
-
- int timeIndex = 0;
- var timeMock = new Mock();
- timeMock.SetupGet(tp => tp.UtcNow).Returns(() => timeValues[timeIndex]);
- //.Callback(() => timeIndex = timeIndex + 1); //Comment while index is not fixed
-
- TimeProvider.Current = timeMock.Object;
- TimeProvider.Current.UtcNow.ToTimestampMS().Should().Be(defaultTimestamp); //1980-06-01 00:00:15:001
-
- //public void Log(string message, LogLevel level)
- //create ILogPlugin for Tests
- /*
- mockConsensusContext.Setup(mr => mr.Log(It.IsAny(), It.IsAny()))
- .Callback((string message, LogLevel level) => {
- Console.WriteLine($"CONSENSUS LOG: {message}");
- }
- );
- */
-
- // Creating a test block
- Header header = new Header();
- TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal);
- header.Size.Should().Be(105);
- Console.WriteLine($"header {header} hash {header.Hash} {header.PrevHash} timestamp {timestampVal}");
- timestampVal.Should().Be(defaultTimestamp);
- TestProbe subscriber = CreateTestProbe();
- TestActorRef actorConsensus = ActorOfAsTestActorRef(
- Akka.Actor.Props.Create(() => (ConsensusService)Activator.CreateInstance(typeof(ConsensusService), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { subscriber, subscriber, subscriber, mockContext.Object }, null))
- );
-
- var testPersistCompleted = new Blockchain.PersistCompleted
- {
- Block = new Block
- {
- Version = header.Version,
- PrevHash = header.PrevHash,
- MerkleRoot = header.MerkleRoot,
- Timestamp = header.Timestamp,
- Index = header.Index,
- NextConsensus = header.NextConsensus,
- Transactions = new Transaction[0]
- }
- };
- Console.WriteLine("\n==========================");
- Console.WriteLine("Telling a new block to actor consensus...");
- Console.WriteLine("will trigger OnPersistCompleted without OnStart flag!");
- // OnPersist will not launch timer, we need OnStart
- actorConsensus.Tell(testPersistCompleted);
- Console.WriteLine("\n==========================");
-
- Console.WriteLine("\n==========================");
- Console.WriteLine("will start consensus!");
- actorConsensus.Tell(new ConsensusService.Start
- {
- IgnoreRecoveryLogs = true
- });
-
- Console.WriteLine("Waiting for subscriber recovery message...");
- // The next line force a waits, then, subscriber keeps running its thread
- // In the next case it waits for a Msg of type LocalNode.SendDirectly
- // As we may expect, as soon as consensus start it sends a RecoveryRequest of this aforementioned type
- var askingForInitialRecovery = subscriber.ExpectMsg();
- Console.WriteLine($"Recovery Message I: {askingForInitialRecovery}");
- foreach (var validator in mockContext.Object.Validators)
- {
- mockContext.Object.LastSeenMessage[validator] = 0;
- }
- // Ensuring cast of type ConsensusPayload from the received message from subscriber
- ExtensiblePayload initialRecoveryPayload = (ExtensiblePayload)askingForInitialRecovery.Inventory;
- // Ensuring casting of type RecoveryRequest
- RecoveryRequest rrm = initialRecoveryPayload.Data.AsSerializable();
- rrm.Timestamp.Should().Be(defaultTimestamp);
-
- Console.WriteLine("Waiting for backup ChangeView... ");
- var backupOnAskingChangeView = subscriber.ExpectMsg();
- 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);
-
- // Original Contract
- Contract originalContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators);
- Console.WriteLine($"\nORIGINAL Contract is: {originalContract.ScriptHash}");
- Console.WriteLine($"ORIGINAL NextConsensus: {mockContext.Object.Block.NextConsensus}\nENSURING values...");
- originalContract.ScriptHash.Should().Be(UInt160.Parse("0xe239c7228fa6b46cc0cf43623b2f934301d0b4f7"));
- mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0xe239c7228fa6b46cc0cf43623b2f934301d0b4f7"));
-
- Console.WriteLine("\n==========================");
- Console.WriteLine("will trigger OnPersistCompleted again with OnStart flag!");
- actorConsensus.Tell(testPersistCompleted);
- Console.WriteLine("\n==========================");
-
- // Disabling flag ViewChanging by reverting cache of changeview that was sent
- mockContext.Object.ChangeViewPayloads[mockContext.Object.MyIndex] = null;
- Console.WriteLine("Forcing Failed nodes for recovery request... ");
- mockContext.Object.CountFailed.Should().Be(0);
- mockContext.Object.LastSeenMessage.Clear();
- mockContext.Object.CountFailed.Should().Be(7);
- Console.WriteLine("\nWaiting for recovery due to failed nodes... ");
- var backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg();
- 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 = 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[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 = (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");
- 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 = (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);
- Console.WriteLine($"\nAsserting CountFailed is 5...");
- mockContext.Object.CountFailed.Should().Be(5);
-
- // Simulating CN 3
- TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 2));
- //Waiting for RecoveryRequest for a more deterministic UT
- backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg();
- recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory;
- rrm = recoveryPayload.Data.AsSerializable();
- rrm.Timestamp.Should().Be(defaultTimestamp);
- //Asserts
- Console.WriteLine("\nAsserting PreparationPayloads count is 3...");
- mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(3);
- Console.WriteLine($"\nAsserting CountFailed is 4...");
- mockContext.Object.CountFailed.Should().Be(4);
-
- // Simulating CN 5
- TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 4));
- //Waiting for RecoveryRequest for a more deterministic UT
- backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg();
- recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory;
- rrm = recoveryPayload.Data.AsSerializable();
- rrm.Timestamp.Should().Be(defaultTimestamp);
- //Asserts
- Console.WriteLine("\nAsserting PreparationPayloads count is 4...");
- mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(4);
- Console.WriteLine($"\nAsserting CountFailed is 3...");
- mockContext.Object.CountFailed.Should().Be(3);
-
- // Simulating CN 4
- TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 3));
- var onCommitPayload = subscriber.ExpectMsg();
- 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...");
- mockContext.Object.CountCommitted.Should().Be(1);
- Console.WriteLine($"\nAsserting CountFailed is 2...");
- mockContext.Object.CountFailed.Should().Be(2);
-
- Console.WriteLine($"ORIGINAL BlockHash: {mockContext.Object.Block.Hash}");
- Console.WriteLine($"ORIGINAL Block NextConsensus: {mockContext.Object.Block.NextConsensus}");
-
- for (int i = 0; i < mockContext.Object.Validators.Length; i++)
- Console.WriteLine($"{mockContext.Object.Validators[i]}/{Contract.CreateSignatureContract(mockContext.Object.Validators[i]).ScriptHash}");
- mockContext.Object.Validators = new ECPoint[7]
- {
- kp_array[0].PublicKey,
- kp_array[1].PublicKey,
- kp_array[2].PublicKey,
- kp_array[3].PublicKey,
- kp_array[4].PublicKey,
- kp_array[5].PublicKey,
- kp_array[6].PublicKey
- };
- Console.WriteLine($"Generated keypairs PKey:");
- //refresh LastSeenMessage
- mockContext.Object.LastSeenMessage.Clear();
- for (int i = 0; i < mockContext.Object.Validators.Length; i++)
- Console.WriteLine($"{mockContext.Object.Validators[i]}/{Contract.CreateSignatureContract(mockContext.Object.Validators[i]).ScriptHash}");
- var updatedContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators);
- Console.WriteLine($"\nContract updated: {updatedContract.ScriptHash}");
-
- // ===============================================================
- CachedCommittee cachedCommittee = new CachedCommittee(mockContext.Object.Validators.Select(p => (p, BigInteger.Zero)));
- mockContext.Object.Snapshot.Storages.Delete(CreateStorageKeyForNativeNeo(14));
- mockContext.Object.Snapshot.Storages.Add(CreateStorageKeyForNativeNeo(14), new StorageItem()
- {
- Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), 4096)
- });
- mockContext.Object.Snapshot.Commit();
- // ===============================================================
-
- // Forcing next consensus
- var originalBlockHashData = mockContext.Object.Block.GetHashData();
- mockContext.Object.Block.NextConsensus = updatedContract.ScriptHash;
- mockContext.Object.Block.Header.NextConsensus = updatedContract.ScriptHash;
- var originalBlockMerkleRoot = mockContext.Object.Block.MerkleRoot;
- Console.WriteLine($"\noriginalBlockMerkleRoot: {originalBlockMerkleRoot}");
- var updatedBlockHashData = mockContext.Object.Block.GetHashData();
- Console.WriteLine($"originalBlockHashData: {originalBlockHashData.ToScriptHash()}");
- Console.WriteLine($"updatedBlockHashData: {updatedBlockHashData.ToScriptHash()}");
-
- Console.WriteLine("\n\n==========================");
- Console.WriteLine("\nBasic commits Signatures verification");
- // Basic tests for understanding signatures and ensuring signatures of commits are correct on tests
-
-
- 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, 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 = (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(mockContext.Object, commitPayload, 5, kp_array[5], updatedBlockHashData));
- tempPayloadToBlockAndWait = subscriber.ExpectMsg();
- 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(mockContext.Object, commitPayload, 4, kp_array[4], updatedBlockHashData));
- tempPayloadToBlockAndWait = subscriber.ExpectMsg();
- Console.WriteLine("\nAsserting CountCommitted is 4...");
- mockContext.Object.CountCommitted.Should().Be(4);
-
- // =============================================
- // 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(mockContext.Object, commitPayload, 3));
- Console.WriteLine("\nWaiting for recovery due to failed nodes... ");
- var backupOnRecoveryMessageAfterCommit = subscriber.ExpectMsg();
- 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...");
- rmm.PreparationMessages.Count().Should().Be(5);
- Console.WriteLine("\nAsserting recovery message CommitMessages is 4...");
- rmm.CommitMessages.Count().Should().Be(4);
- // =============================================
-
- Console.WriteLine($"\nForcing block {mockContext.Object.Block.GetHashData().ToScriptHash()} PrevHash to UInt256.Zero");
- // Another option would be to manipulate Blockchain.Singleton.GetSnapshot().Blocks.GetAndChange
- // We would need to get the PrevHash and change the NextConsensus field
- var oldPrevHash = mockContext.Object.Block.PrevHash;
- mockContext.Object.Block.PrevHash = UInt256.Zero;
- //Payload should also be forced, otherwise OnConsensus will not pass
- 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
- mockContext.Object.Block.Witness = new Witness { };
- mockContext.Object.Block.Witness.VerificationScript = updatedContract.Script;
- Console.WriteLine($"\nUpdating BlockBase Witness scripthash to: {mockContext.Object.Block.Witness.ScriptHash}");
- Console.WriteLine($"\nNew Hash is {mockContext.Object.Block.GetHashData().ToScriptHash()}");
-
- Console.WriteLine("\nCN4 simulation time - Final needed signatures");
- TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData()));
-
- Console.WriteLine("\nWait for subscriber Block");
- var utBlock = subscriber.ExpectMsg();
- Console.WriteLine("\nAsserting CountCommitted is 5...");
- mockContext.Object.CountCommitted.Should().Be(5);
-
- Console.WriteLine($"\nAsserting block NextConsensus..{utBlock.NextConsensus}");
- utBlock.NextConsensus.Should().Be(updatedContract.ScriptHash);
- Console.WriteLine("\n==========================");
-
- // =============================================
- Console.WriteLine("\nRecovery simulation...");
- mockContext.Object.CommitPayloads = new ExtensiblePayload[mockContext.Object.Validators.Length];
- // avoiding the BlockSent flag
- mockContext.Object.Block.Transactions = null;
- // ensuring same hash as snapshot
- mockContext.Object.Block.PrevHash = oldPrevHash;
-
- Console.WriteLine("\nAsserting CountCommitted is 0...");
- mockContext.Object.CountCommitted.Should().Be(0);
- Console.WriteLine($"\nAsserting CountFailed is 0...");
- mockContext.Object.CountFailed.Should().Be(3);
- Console.WriteLine($"\nModifying CountFailed and asserting 7...");
- // This will ensure a non-deterministic behavior after last recovery
- mockContext.Object.LastSeenMessage.Clear();
- mockContext.Object.CountFailed.Should().Be(7);
-
- TellConsensusPayload(actorConsensus, rmPayload);
-
- Console.WriteLine("\nWaiting for RecoveryRequest before final asserts...");
- var onRecoveryRequestAfterRecovery = subscriber.ExpectMsg();
- 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)...");
- mockContext.Object.CountCommitted.Should().Be(3);
- // =============================================
-
- // =============================================
- // ============================================================================
- // finalize ConsensusService actor
- // ============================================================================
- Console.WriteLine("Returning states.");
- // Updating context.Snapshot with the one that was committed
- Console.WriteLine("mockContext Reset for returning Blockchain.Singleton snapshot to original state.");
- mockContext.Object.Reset(0);
- mockContext.Object.Snapshot.Storages.Delete(CreateStorageKeyForNativeNeo(14));
- cachedCommittee = new CachedCommittee(Blockchain.StandbyCommittee.Select(p => (p, BigInteger.Zero)));
- mockContext.Object.Snapshot.Storages.Add(CreateStorageKeyForNativeNeo(14), new StorageItem
- {
- Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), 4096)
- });
- mockContext.Object.Snapshot.Commit();
-
- Console.WriteLine("mockContext Reset.");
- mockContext.Object.Reset(0);
- Console.WriteLine("TimeProvider Reset.");
- TimeProvider.ResetToDefault();
-
- Console.WriteLine("Finalizing consensus service actor.");
- Sys.Stop(actorConsensus);
- Console.WriteLine("Actor actorConsensus Stopped.\n");
- }
-
- ///
- /// Get a clone of a ConsensusPayload that contains a Commit Message, change its currentValidatorIndex and sign it
- ///
- /// ConsensusPayload that will be modified
- /// 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 ExtensiblePayload GetCommitPayloadModifiedAndSignedCopy(ConsensusContext context, ExtensiblePayload cpToCopy, byte vI, KeyPair kp, byte[] blockHashToSign)
- {
- var cpCommitTemp = cpToCopy.ToArray().AsSerializable();
- cpCommitTemp.Sender = context.GetSender(vI);
- var oldMessage = cpCommitTemp.Data.AsSerializable();
- cpCommitTemp.Data = new Commit
- {
- 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;
- }
-
- ///
- /// Get a clone of a ConsensusPayload and change its currentValidatorIndex
- ///
- /// ConsensusPayload that will be modified
- /// new ValidatorIndex for the cpToCopy
- public ExtensiblePayload GetPayloadAndModifyValidator(ConsensusContext context, ExtensiblePayload cpToCopy, byte 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(ExtensiblePayload payload, KeyPair kp)
- {
- ContractParametersContext sc;
- try
- {
- sc = new ContractParametersContext(payload);
- byte[] signature = sc.Verifiable.Sign(kp);
- sc.AddSignature(Contract.CreateSignatureContract(kp.PublicKey), kp.PublicKey, signature);
- }
- catch (InvalidOperationException)
- {
- return;
- }
- payload.Witness = sc.GetWitnesses()[0];
- }
-
- [TestMethod]
- public void TestSerializeAndDeserializeConsensusContext()
- {
- var consensusContext = new ConsensusContext(null, null)
- {
- Block = new Block
- {
- PrevHash = Blockchain.GenesisBlock.Hash,
- Index = 1,
- Timestamp = 4244941711,
- NextConsensus = UInt160.Parse("5555AAAA5555AAAA5555AAAA5555AAAA5555AAAA"),
- ConsensusData = new ConsensusData
- {
- PrimaryIndex = 6
- }
- },
- ViewNumber = 2,
- Validators = new ECPoint[7]
- {
- 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)
- },
- MyIndex = -1
- };
- var testTx1 = TestUtils.CreateRandomHashTransaction();
- var testTx2 = TestUtils.CreateRandomHashTransaction();
-
- int txCountToInlcude = 256;
- consensusContext.TransactionHashes = new UInt256[txCountToInlcude];
-
- Transaction[] txs = new Transaction[txCountToInlcude];
- for (int i = 0; i < txCountToInlcude; i++)
- {
- txs[i] = TestUtils.CreateRandomHashTransaction();
- consensusContext.TransactionHashes[i] = txs[i].Hash;
- }
- // consensusContext.TransactionHashes = new UInt256[2] {testTx1.Hash, testTx2.Hash};
- consensusContext.Transactions = txs.ToDictionary(p => p.Hash);
-
- consensusContext.PreparationPayloads = new ExtensiblePayload[consensusContext.Validators.Length];
- var prepareRequestMessage = new PrepareRequest
- {
- PrevHash = consensusContext.Block.PrevHash,
- TransactionHashes = consensusContext.TransactionHashes,
- Timestamp = 23
- };
- consensusContext.PreparationPayloads[6] = MakeSignedPayload(consensusContext, prepareRequestMessage, 6, new[] { (byte)'3', (byte)'!' });
- consensusContext.PreparationPayloads[0] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash }, 0, new[] { (byte)'t', (byte)'e' });
- consensusContext.PreparationPayloads[1] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash }, 1, new[] { (byte)'s', (byte)'t' });
- consensusContext.PreparationPayloads[2] = null;
- consensusContext.PreparationPayloads[3] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash }, 3, new[] { (byte)'1', (byte)'2' });
- consensusContext.PreparationPayloads[4] = null;
- consensusContext.PreparationPayloads[5] = null;
-
- 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' });
- consensusContext.CommitPayloads[6] = MakeSignedPayload(consensusContext, new Commit { Signature = sha256.ComputeHash(testTx2.Hash.ToArray()).Concat(sha256.ComputeHash(testTx2.Hash.ToArray())).ToArray() }, 3, new[] { (byte)'6', (byte)'7' });
- }
-
- consensusContext.Block.Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS();
-
- 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;
- consensusContext.ChangeViewPayloads[3] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = uint.MaxValue }, 3, new[] { (byte)'C' });
- consensusContext.ChangeViewPayloads[4] = null;
- consensusContext.ChangeViewPayloads[5] = null;
- consensusContext.ChangeViewPayloads[6] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 1 }, 6, new[] { (byte)'D' });
-
- consensusContext.LastChangeViewPayloads = new ExtensiblePayload[consensusContext.Validators.Length];
-
- var copiedContext = TestUtils.CopyMsgBySerialization(consensusContext, new ConsensusContext(null, null));
-
- copiedContext.Block.PrevHash.Should().Be(consensusContext.Block.PrevHash);
- copiedContext.Block.Index.Should().Be(consensusContext.Block.Index);
- copiedContext.ViewNumber.Should().Be(consensusContext.ViewNumber);
- copiedContext.Validators.Should().BeEquivalentTo(consensusContext.Validators);
- copiedContext.MyIndex.Should().Be(consensusContext.MyIndex);
- copiedContext.Block.ConsensusData.PrimaryIndex.Should().Be(consensusContext.Block.ConsensusData.PrimaryIndex);
- copiedContext.Block.Timestamp.Should().Be(consensusContext.Block.Timestamp);
- copiedContext.Block.NextConsensus.Should().Be(consensusContext.Block.NextConsensus);
- copiedContext.TransactionHashes.Should().BeEquivalentTo(consensusContext.TransactionHashes);
- copiedContext.Transactions.Should().BeEquivalentTo(consensusContext.Transactions);
- copiedContext.Transactions.Values.Should().BeEquivalentTo(consensusContext.Transactions.Values);
- copiedContext.PreparationPayloads.Should().BeEquivalentTo(consensusContext.PreparationPayloads);
- copiedContext.CommitPayloads.Should().BeEquivalentTo(consensusContext.CommitPayloads);
- copiedContext.ChangeViewPayloads.Should().BeEquivalentTo(consensusContext.ChangeViewPayloads);
- }
-
- [TestMethod]
- public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepareRequest()
- {
- var msg = new RecoveryMessage
- {
- ChangeViewMessages = new Dictionary()
- {
- {
- 0,
- new RecoveryMessage.ChangeViewPayloadCompact
- {
- ValidatorIndex = 0,
- OriginalViewNumber = 9,
- Timestamp = 6,
- InvocationScript = new[] { (byte)'A' }
- }
- },
- {
- 1,
- new RecoveryMessage.ChangeViewPayloadCompact
- {
- ValidatorIndex = 1,
- OriginalViewNumber = 7,
- Timestamp = 5,
- InvocationScript = new[] { (byte)'B' }
- }
- },
- {
- 3,
- new RecoveryMessage.ChangeViewPayloadCompact
- {
- ValidatorIndex = 3,
- OriginalViewNumber = 5,
- Timestamp = 3,
- InvocationScript = new[] { (byte)'C' }
- }
- },
- {
- 6,
- new RecoveryMessage.ChangeViewPayloadCompact
- {
- ValidatorIndex = 6,
- OriginalViewNumber = 2,
- Timestamp = 1,
- InvocationScript = new[] { (byte)'D' }
- }
- }
- },
- PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })),
- PreparationMessages = new Dictionary()
- {
- {
- 0,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 0,
- InvocationScript = new[] { (byte)'t', (byte)'e' }
- }
- },
- {
- 3,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 3,
- InvocationScript = new[] { (byte)'1', (byte)'2' }
- }
- },
- {
- 6,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 6,
- InvocationScript = new[] { (byte)'3', (byte)'!' }
- }
- }
- },
- CommitMessages = new Dictionary()
- };
-
- // msg.TransactionHashes = null;
- // msg.Nonce = 0;
- // msg.NextConsensus = null;
- // msg.MinerTransaction = (MinerTransaction) null;
- msg.PrepareRequestMessage.Should().Be(null);
-
- var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ;
-
- copiedMsg.ChangeViewMessages.Should().BeEquivalentTo(msg.ChangeViewMessages);
- copiedMsg.PreparationHash.Should().Be(msg.PreparationHash);
- copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages);
- copiedMsg.CommitMessages.Count.Should().Be(0);
- }
-
- [TestMethod]
- public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareRequest()
- {
- Transaction[] txs = new Transaction[5];
- for (int i = 0; i < txs.Length; i++)
- txs[i] = TestUtils.CreateRandomHashTransaction();
- var msg = new RecoveryMessage
- {
- ChangeViewMessages = new Dictionary()
- {
- {
- 0,
- new RecoveryMessage.ChangeViewPayloadCompact
- {
- ValidatorIndex = 0,
- OriginalViewNumber = 9,
- Timestamp = 6,
- InvocationScript = new[] { (byte)'A' }
- }
- },
- {
- 1,
- new RecoveryMessage.ChangeViewPayloadCompact
- {
- ValidatorIndex = 1,
- OriginalViewNumber = 7,
- Timestamp = 5,
- InvocationScript = new[] { (byte)'B' }
- }
- },
- {
- 3,
- new RecoveryMessage.ChangeViewPayloadCompact
- {
- ValidatorIndex = 3,
- OriginalViewNumber = 5,
- Timestamp = 3,
- InvocationScript = new[] { (byte)'C' }
- }
- },
- {
- 6,
- new RecoveryMessage.ChangeViewPayloadCompact
- {
- ValidatorIndex = 6,
- OriginalViewNumber = 2,
- Timestamp = 1,
- InvocationScript = new[] { (byte)'D' }
- }
- }
- },
- PrepareRequestMessage = new PrepareRequest
- {
- PrevHash = UInt256.Zero,
- TransactionHashes = txs.Select(p => p.Hash).ToArray()
- },
- PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })),
- PreparationMessages = new Dictionary()
- {
- {
- 0,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 0,
- InvocationScript = new[] { (byte)'t', (byte)'e' }
- }
- },
- {
- 1,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 1,
- InvocationScript = new[] { (byte)'s', (byte)'t' }
- }
- },
- {
- 3,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 3,
- InvocationScript = new[] { (byte)'1', (byte)'2' }
- }
- }
- },
- CommitMessages = new Dictionary()
- };
-
- var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ;
-
- copiedMsg.ChangeViewMessages.Should().BeEquivalentTo(msg.ChangeViewMessages);
- copiedMsg.PrepareRequestMessage.Should().BeEquivalentTo(msg.PrepareRequestMessage);
- copiedMsg.PreparationHash.Should().Be(null);
- copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages);
- copiedMsg.CommitMessages.Count.Should().Be(0);
- }
-
- [TestMethod]
- public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutCommits()
- {
- Transaction[] txs = new Transaction[5];
- for (int i = 0; i < txs.Length; i++)
- txs[i] = TestUtils.CreateRandomHashTransaction();
- var msg = new RecoveryMessage
- {
- ChangeViewMessages = new Dictionary(),
- PrepareRequestMessage = new PrepareRequest
- {
- PrevHash = UInt256.Zero,
- TransactionHashes = txs.Select(p => p.Hash).ToArray()
- },
- PreparationMessages = new Dictionary()
- {
- {
- 0,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 0,
- InvocationScript = new[] { (byte)'t', (byte)'e' }
- }
- },
- {
- 1,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 1,
- InvocationScript = new[] { (byte)'s', (byte)'t' }
- }
- },
- {
- 3,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 3,
- InvocationScript = new[] { (byte)'1', (byte)'2' }
- }
- },
- {
- 6,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 6,
- InvocationScript = new[] { (byte)'3', (byte)'!' }
- }
- }
- },
- CommitMessages = new Dictionary()
- };
-
- var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ;
-
- copiedMsg.ChangeViewMessages.Count.Should().Be(0);
- copiedMsg.PrepareRequestMessage.Should().BeEquivalentTo(msg.PrepareRequestMessage);
- copiedMsg.PreparationHash.Should().Be(null);
- copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages);
- copiedMsg.CommitMessages.Count.Should().Be(0);
- }
-
- [TestMethod]
- public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithCommits()
- {
- Transaction[] txs = new Transaction[5];
- for (int i = 0; i < txs.Length; i++)
- txs[i] = TestUtils.CreateRandomHashTransaction();
- var msg = new RecoveryMessage
- {
- ChangeViewMessages = new Dictionary(),
- PrepareRequestMessage = new PrepareRequest
- {
- PrevHash = UInt256.Zero,
- TransactionHashes = txs.Select(p => p.Hash).ToArray()
- },
- PreparationMessages = new Dictionary()
- {
- {
- 0,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 0,
- InvocationScript = new[] { (byte)'t', (byte)'e' }
- }
- },
- {
- 1,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 1,
- InvocationScript = new[] { (byte)'s', (byte)'t' }
- }
- },
- {
- 3,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 3,
- InvocationScript = new[] { (byte)'1', (byte)'2' }
- }
- },
- {
- 6,
- new RecoveryMessage.PreparationPayloadCompact
- {
- ValidatorIndex = 6,
- InvocationScript = new[] { (byte)'3', (byte)'!' }
- }
- }
- },
- CommitMessages = new Dictionary
- {
- {
- 1,
- new RecoveryMessage.CommitPayloadCompact
- {
- ValidatorIndex = 1,
- Signature = new byte[64] { (byte)'1', (byte)'2', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- InvocationScript = new[] { (byte)'1', (byte)'2' }
- }
- },
- {
- 6,
- new RecoveryMessage.CommitPayloadCompact
- {
- ValidatorIndex = 6,
- Signature = new byte[64] { (byte)'3', (byte)'D', (byte)'R', (byte)'I', (byte)'N', (byte)'K', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
- InvocationScript = new[] { (byte)'6', (byte)'7' }
- }
- }
- }
- };
-
- var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ;
-
- copiedMsg.ChangeViewMessages.Count.Should().Be(0);
- copiedMsg.PrepareRequestMessage.Should().BeEquivalentTo(msg.PrepareRequestMessage);
- copiedMsg.PreparationHash.Should().Be(null);
- copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages);
- copiedMsg.CommitMessages.Should().BeEquivalentTo(msg.CommitMessages);
- }
-
- private static ExtensiblePayload MakeSignedPayload(ConsensusContext context, ConsensusMessage message, byte validatorIndex, byte[] witnessInvocationScript)
- {
- message.BlockIndex = context.Block.Index;
- message.ValidatorIndex = validatorIndex;
- return new ExtensiblePayload
- {
- Category = "Consensus",
- ValidBlockStart = 0,
- ValidBlockEnd = message.BlockIndex,
- Sender = context.GetSender(validatorIndex),
- Data = message.ToArray(),
- Witness = new Witness
- {
- InvocationScript = witnessInvocationScript,
- VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[validatorIndex])
- }
- };
- }
-
- private StorageKey CreateStorageKeyForNativeNeo(byte prefix)
- {
- StorageKey storageKey = new StorageKey
- {
- Id = NativeContract.NEO.Id,
- Key = new byte[sizeof(byte)]
- };
- storageKey.Key[0] = prefix;
- return storageKey;
- }
-
- private void TellConsensusPayload(IActorRef actor, ExtensiblePayload payload)
- {
- actor.Tell(new Blockchain.RelayResult
- {
- Inventory = payload,
- Result = VerifyResult.Succeed
- });
- }
- }
-}
diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs
deleted file mode 100644
index 297ad0e17c..0000000000
--- a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs
+++ /dev/null
@@ -1,193 +0,0 @@
-using Akka.TestKit.Xunit2;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Moq;
-using Neo.Consensus;
-using Neo.IO;
-using Neo.Ledger;
-using Neo.Network.P2P.Payloads;
-using Neo.SmartContract.Native;
-using Neo.Wallets;
-using System;
-using System.Linq;
-
-namespace Neo.UnitTests.Consensus
-{
- [TestClass]
- public class UT_ConsensusContext : TestKit
- {
- ConsensusContext _context;
- KeyPair[] _validatorKeys;
-
- [TestInitialize]
- public void TestSetup()
- {
- TestBlockchain.InitializeMockNeoSystem();
-
- var rand = new Random();
- var mockWallet = new Mock();
- mockWallet.Setup(p => p.GetAccount(It.IsAny())).Returns(p => new TestWalletAccount(p));
-
- // Create dummy validators
-
- _validatorKeys = new KeyPair[7];
- for (int x = 0; x < _validatorKeys.Length; x++)
- {
- var pk = new byte[32];
- rand.NextBytes(pk);
-
- _validatorKeys[x] = new KeyPair(pk);
- }
-
- _context = new ConsensusContext(mockWallet.Object, Blockchain.Singleton.Store)
- {
- Validators = _validatorKeys.Select(u => u.PublicKey).ToArray()
- };
- _context.Reset(0);
- }
-
- [TestCleanup]
- public void Cleanup()
- {
- Shutdown();
- }
-
- [TestMethod]
- public void TestMaxBlockSize_Good()
- {
- // Only one tx, is included
-
- var tx1 = CreateTransactionWithSize(200);
- _context.EnsureMaxBlockLimitation(new Transaction[] { tx1 });
- EnsureContext(_context, tx1);
-
- // All txs included
-
- var max = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(_context.Snapshot);
- var txs = new Transaction[max];
-
- for (int x = 0; x < max; x++) txs[x] = CreateTransactionWithSize(100);
-
- _context.EnsureMaxBlockLimitation(txs);
- EnsureContext(_context, txs);
- }
-
- [TestMethod]
- public void TestMaxBlockSize_Exceed()
- {
- // Two tx, the last one exceed the size rule, only the first will be included
-
- var tx1 = CreateTransactionWithSize(200);
- var tx2 = CreateTransactionWithSize(256 * 1024);
- _context.EnsureMaxBlockLimitation(new Transaction[] { tx1, tx2 });
- EnsureContext(_context, tx1);
-
- // Exceed txs number, just MaxTransactionsPerBlock included
-
- var max = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(_context.Snapshot);
- var txs = new Transaction[max + 1];
-
- for (int x = 0; x < max; x++) txs[x] = CreateTransactionWithSize(100);
-
- _context.EnsureMaxBlockLimitation(txs);
- EnsureContext(_context, txs.Take(max).ToArray());
- }
-
- [TestMethod]
- public void TestMaxBlockSytemFee()
- {
- var tx1 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2);
-
- // Less than MaxBlockSystemFee
- _context.EnsureMaxBlockLimitation(new Transaction[] { tx1 });
- EnsureContext(_context, new Transaction[] { tx1 });
-
- // Equal MaxBlockSystemFee
- tx1 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 + 1);
- var tx2 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 - 1);
-
- _context.EnsureMaxBlockLimitation(new Transaction[] { tx1, tx2 });
- EnsureContext(_context, new Transaction[] { tx1, tx2 });
-
- // Exceed MaxBlockSystemFee
- tx1 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 + 3);
- tx2 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 - 3);
- var tx3 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 - 4);
-
- _context.EnsureMaxBlockLimitation(new Transaction[] { tx1, tx2, tx3 });
- EnsureContext(_context, new Transaction[] { tx1, tx2 });
- }
-
- private Transaction CreateTransactionWithSize(int v)
- {
- var r = new Random();
- var tx = new Transaction()
- {
- Attributes = System.Array.Empty(),
- Signers = new Signer[] { new Signer() { Account = UInt160.Zero } },
- NetworkFee = 0,
- Nonce = (uint)Environment.TickCount,
- Script = new byte[0],
- SystemFee = 0,
- ValidUntilBlock = (uint)r.Next(),
- Version = 0,
- Witnesses = new Witness[0],
- };
-
- // Could be higher (few bytes) if varSize grows
- tx.Script = new byte[v - tx.Size];
- return tx;
- }
-
- private Transaction CreateTransactionWithSytemFee(long fee)
- {
- var tx = new Transaction()
- {
- Attributes = System.Array.Empty(),
- Signers = new Signer[] { new Signer() { Account = UInt160.Zero } },
- NetworkFee = 0,
- Nonce = (uint)Environment.TickCount,
- Script = new byte[0],
- SystemFee = fee,
- ValidUntilBlock = int.MaxValue,
- Version = 0,
- Witnesses = new Witness[0],
- };
- return tx;
- }
-
- private Block SignBlock(ConsensusContext context)
- {
- context.Block.MerkleRoot = null;
-
- // Fake commits
-
- for (int x = 0; x < _validatorKeys.Length; x++)
- {
- _context.MyIndex = x;
-
- var com = _context.MakeCommit();
- _context.CommitPayloads[_context.MyIndex] = com;
- }
-
- return context.CreateBlock();
- }
-
- private void EnsureContext(ConsensusContext context, params Transaction[] expected)
- {
- // Check all tx
-
- Assert.AreEqual(expected.Length, context.Transactions.Count);
- Assert.IsTrue(expected.All(tx => context.Transactions.ContainsKey(tx.Hash)));
-
- Assert.AreEqual(expected.Length, context.TransactionHashes.Length);
- Assert.IsTrue(expected.All(tx => context.TransactionHashes.Count(t => t == tx.Hash) == 1));
-
- // Ensure length
-
- var block = SignBlock(context);
-
- Assert.AreEqual(context.GetExpectedBlockSize(), block.Size);
- Assert.IsTrue(block.Size < NativeContract.Policy.GetMaxBlockSize(context.Snapshot));
- }
- }
-}
diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs
deleted file mode 100644
index 9de9617e65..0000000000
--- a/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using Akka.TestKit.Xunit2;
-using FluentAssertions;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Neo.Consensus;
-using Neo.Ledger;
-using Neo.Network.P2P.Payloads;
-using System;
-
-namespace Neo.UnitTests.Consensus
-{
- [TestClass]
- public class UT_ConsensusServiceMailbox : TestKit
- {
- private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism
-
- ConsensusServiceMailbox uut;
-
- [TestCleanup]
- public void Cleanup()
- {
- Shutdown();
- }
-
- [TestInitialize]
- public void TestSetup()
- {
- Akka.Actor.ActorSystem system = Sys;
- var config = TestKit.DefaultConfig;
- var akkaSettings = new Akka.Actor.Settings(system, config);
- uut = new ConsensusServiceMailbox(akkaSettings, config);
- }
-
- [TestMethod]
- public void ConsensusServiceMailbox_Test_IsHighPriority()
- {
- // high priority
- 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);
-
- // any random object should not have priority
- object obj = null;
- uut.IsHighPriority(obj).Should().Be(false);
- }
- }
-}
diff --git a/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs b/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs
deleted file mode 100644
index 3c66f59533..0000000000
--- a/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using FluentAssertions;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using Neo.Consensus;
-using Neo.IO;
-
-namespace Neo.UnitTests.Consensus
-{
- [TestClass]
- public class UT_RecoveryRequest
- {
- [TestMethod]
- public void Size_Get()
- {
- var test = new RecoveryRequest() { Timestamp = 1, ViewNumber = 1 };
- test.Size.Should().Be(15);
- }
-
- [TestMethod]
- public void DeserializeAndSerialize()
- {
- var test = new RecoveryRequest() { ViewNumber = 1, Timestamp = 123 };
- var clone = test.ToArray().AsSerializable();
-
- Assert.AreEqual(test.Timestamp, clone.Timestamp);
- Assert.AreEqual(test.Type, clone.Type);
- Assert.AreEqual(test.ViewNumber, clone.ViewNumber);
- }
- }
-}
diff --git a/tests/neo.UnitTests/UT_NeoSystem.cs b/tests/neo.UnitTests/UT_NeoSystem.cs
index 5e440d8374..646d214f6c 100644
--- a/tests/neo.UnitTests/UT_NeoSystem.cs
+++ b/tests/neo.UnitTests/UT_NeoSystem.cs
@@ -22,8 +22,5 @@ public void Setup()
[TestMethod]
public void TestGetTaskManager() => neoSystem.TaskManager.Should().NotBeNull();
-
- [TestMethod]
- public void TestGetConsensus() => neoSystem.Consensus.Should().BeNull();
}
}