From 999251c0feaad3d4617aa061f7176afa6804e054 Mon Sep 17 00:00:00 2001 From: jsolman Date: Thu, 24 Jan 2019 18:50:32 -0800 Subject: [PATCH 01/34] Save consensus context each time we change view (so we will be close to correct view on restart). --- neo/Consensus/ConsensusService.cs | 8 +++++--- neo/Persistence/LevelDB/Prefixes.cs | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 164f3c7b77..41d9d67480 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -13,6 +13,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Neo.Persistence.LevelDB; namespace Neo.Consensus { @@ -22,7 +23,7 @@ public class Start { } public class SetViewNumber { public byte ViewNumber; } internal class Timer { public uint Height; public byte ViewNumber; } - private const byte ContextSerializationPrefix = 0xf4; + private const byte ContextSerializationPrefix = Prefixes.CN_CONTEXT; private readonly IConsensusContext context; private readonly IActorRef localNode; @@ -107,6 +108,8 @@ private void CheckExpectedView(byte view_number) if (context.ExpectedView.Count(p => p == view_number) >= context.M) { InitializeConsensus(view_number); + // Save our view so if we crash and come back we will be closer to the correct view. + store.Put(ContextSerializationPrefix, new byte[0], context.ToArray()); } } @@ -271,7 +274,6 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m } else { - if (Blockchain.Singleton.MemPool.TryGetValue(hash, out tx)) unverified.Add(tx); } @@ -347,7 +349,7 @@ private void OnStart() if (context.State.HasFlag(ConsensusState.CommitSent)) CheckPreparations(); else - InitializeConsensus(0); + InitializeConsensus(context.ViewNumber); } private void OnTimer(Timer timer) diff --git a/neo/Persistence/LevelDB/Prefixes.cs b/neo/Persistence/LevelDB/Prefixes.cs index a92363f38f..75ffd1fddc 100644 --- a/neo/Persistence/LevelDB/Prefixes.cs +++ b/neo/Persistence/LevelDB/Prefixes.cs @@ -19,5 +19,6 @@ internal static class Prefixes public const byte IX_CurrentHeader = 0xc1; public const byte SYS_Version = 0xf0; + public const byte CN_CONTEXT = 0xf4; } } From 2ce8b71ec7d96a9fb066ed6b41b95fc7e79710c0 Mon Sep 17 00:00:00 2001 From: jsolman Date: Thu, 24 Jan 2019 19:51:36 -0800 Subject: [PATCH 02/34] Add extension methods for loading and saving the ConsensusContext to the Store. --- neo/Consensus/ConsensusContext.cs | 1 + neo/Consensus/ConsensusService.cs | 17 +++-------------- neo/Consensus/Helper.cs | 28 ++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 neo/Consensus/Helper.cs diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 59a8d03a43..bb135815da 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -11,6 +11,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Neo.Persistence.LevelDB; namespace Neo.Consensus { diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 41d9d67480..6aceaaeb08 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -13,7 +13,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Neo.Persistence.LevelDB; namespace Neo.Consensus { @@ -23,8 +22,6 @@ public class Start { } public class SetViewNumber { public byte ViewNumber; } internal class Timer { public uint Height; public byte ViewNumber; } - private const byte ContextSerializationPrefix = Prefixes.CN_CONTEXT; - private readonly IConsensusContext context; private readonly IActorRef localNode; private readonly IActorRef taskManager; @@ -109,7 +106,7 @@ private void CheckExpectedView(byte view_number) { InitializeConsensus(view_number); // Save our view so if we crash and come back we will be closer to the correct view. - store.Put(ContextSerializationPrefix, new byte[0], context.ToArray()); + context.WriteContextToStore(store); } } @@ -120,7 +117,7 @@ private void CheckPreparations() ConsensusPayload payload = context.MakeCommit(); Log($"send commit"); context.State |= ConsensusState.CommitSent; - store.Put(ContextSerializationPrefix, new byte[0], context.ToArray()); + context.WriteContextToStore(store); localNode.Tell(new LocalNode.SendDirectly { Inventory = payload }); CheckCommits(); } @@ -337,15 +334,7 @@ private void OnStart() { Log("OnStart"); started = true; - byte[] data = store.Get(ContextSerializationPrefix, new byte[0]); - if (data != null) - { - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader reader = new BinaryReader(ms)) - { - context.Deserialize(reader); - } - } + context.LoadContextFromStore(store); if (context.State.HasFlag(ConsensusState.CommitSent)) CheckPreparations(); else diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs new file mode 100644 index 0000000000..6afde778e6 --- /dev/null +++ b/neo/Consensus/Helper.cs @@ -0,0 +1,28 @@ +using System.IO; +using Neo.IO; +using Neo.Persistence; +using Neo.Persistence.LevelDB; + +namespace Neo.Consensus +{ + public static class Helper + { + internal static void WriteContextToStore(this IConsensusContext context, Store store) + { + store.Put(Prefixes.CN_CONTEXT, new byte[0], context.ToArray()); + } + + internal static void LoadContextFromStore(this IConsensusContext context, Store store) + { + byte[] data = store.Get(Prefixes.CN_CONTEXT, new byte[0]); + if (data != null) + { + using (MemoryStream ms = new MemoryStream(data, false)) + using (BinaryReader reader = new BinaryReader(ms)) + { + context.Deserialize(reader); + } + } + } + } +} \ No newline at end of file From ae56b38ff065f011af553bfd9a5abc4c4ff557ba Mon Sep 17 00:00:00 2001 From: jsolman Date: Thu, 24 Jan 2019 20:00:09 -0800 Subject: [PATCH 03/34] Load MemoryPool initially with transactions from saved consensus. --- neo/Ledger/MemoryPool.cs | 8 ++++++++ neo/NeoSystem.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 0d857495de..959f864a02 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Threading; using Akka.Util.Internal; +using Neo.Consensus; using Neo.Network.P2P; using Neo.Persistence; using Neo.Plugins; @@ -107,6 +108,13 @@ public MemoryPool(NeoSystem system, int capacity) _system = system; Capacity = capacity; LoadMaxTxLimitsFromPolicyPlugins(); + IConsensusContext context = new ConsensusContext(null); + context.LoadContextFromStore(system.store); + if (context.Transactions == null) return; + foreach (var tx in context.Transactions.Values) + { + TryAdd(tx.Hash, tx); + } } public void LoadMaxTxLimitsFromPolicyPlugins() diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index d5d089dca6..fcd7d53876 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -26,7 +26,7 @@ public class NeoSystem : IDisposable public IActorRef Consensus { get; private set; } public RpcServer RpcServer { get; private set; } - private readonly Store store; + internal readonly Store store; private Peer.Start start_message = null; private bool suspend = false; From 232e2fd1fb5d3c30a181c361369ad1ee281c1a58 Mon Sep 17 00:00:00 2001 From: jsolman Date: Thu, 24 Jan 2019 20:09:52 -0800 Subject: [PATCH 04/34] Fix tests. --- neo.UnitTests/UT_MemoryPool.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index 75accde851..2a426647fb 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -9,6 +9,7 @@ using Neo.IO.Wrappers; using Neo.Network.P2P.Payloads; using Neo.Persistence; +using Neo.Persistence.LevelDB; namespace Neo.UnitTests { @@ -74,6 +75,7 @@ public void TestSetup() mockStore.Setup(p => p.GetBlockHashIndex()).Returns(new TestMetaDataCache()); mockStore.Setup(p => p.GetHeaderHashIndex()).Returns(new TestMetaDataCache()); mockStore.Setup(p => p.GetSnapshot()).Returns(mockSnapshot.Object); + mockStore.Setup(p => p.Get(Prefixes.CN_CONTEXT, It.IsAny())).Returns((byte[]) null); Console.WriteLine("initialize NeoSystem"); TheNeoSystem = new NeoSystem(mockStore.Object); // new Mock(mockStore.Object); From 4ae84f7df3f5d63b8180698f9d885257c9db0a78 Mon Sep 17 00:00:00 2001 From: jsolman Date: Fri, 25 Jan 2019 09:38:07 -0800 Subject: [PATCH 05/34] Remove unused 'using' statements. --- neo/Consensus/ConsensusContext.cs | 1 - neo/Consensus/ConsensusService.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 98e3eacdd2..f840393502 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -11,7 +11,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Neo.Persistence.LevelDB; namespace Neo.Consensus { diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 419faf24fa..b7294f1606 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -11,7 +11,6 @@ using Neo.Wallets; using System; using System.Collections.Generic; -using System.IO; using System.Linq; namespace Neo.Consensus From dc7482c88d9a4fca2ed2b226f1f4ef89ef75a571 Mon Sep 17 00:00:00 2001 From: jsolman Date: Mon, 28 Jan 2019 18:38:54 -0800 Subject: [PATCH 06/34] Pass consensus store into the NeoSystem constructor instead of StartConsensus() method. --- neo.UnitTests/UT_MemoryPool.cs | 2 +- neo/Consensus/Helper.cs | 4 ++-- neo/Ledger/MemoryPool.cs | 2 +- neo/NeoSystem.cs | 8 +++++--- neo/Persistence/LevelDB/Prefixes.cs | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index 2a426647fb..ddf47a1b6c 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -75,7 +75,7 @@ public void TestSetup() mockStore.Setup(p => p.GetBlockHashIndex()).Returns(new TestMetaDataCache()); mockStore.Setup(p => p.GetHeaderHashIndex()).Returns(new TestMetaDataCache()); mockStore.Setup(p => p.GetSnapshot()).Returns(mockSnapshot.Object); - mockStore.Setup(p => p.Get(Prefixes.CN_CONTEXT, It.IsAny())).Returns((byte[]) null); + mockStore.Setup(p => p.Get(Prefixes.CN_Context, It.IsAny())).Returns((byte[]) null); Console.WriteLine("initialize NeoSystem"); TheNeoSystem = new NeoSystem(mockStore.Object); // new Mock(mockStore.Object); diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index 6afde778e6..b806fcc697 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -9,12 +9,12 @@ public static class Helper { internal static void WriteContextToStore(this IConsensusContext context, Store store) { - store.Put(Prefixes.CN_CONTEXT, new byte[0], context.ToArray()); + store.Put(Prefixes.CN_Context, new byte[0], context.ToArray()); } internal static void LoadContextFromStore(this IConsensusContext context, Store store) { - byte[] data = store.Get(Prefixes.CN_CONTEXT, new byte[0]); + byte[] data = store.Get(Prefixes.CN_Context, new byte[0]); if (data != null) { using (MemoryStream ms = new MemoryStream(data, false)) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 959f864a02..a74fcdd5ba 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -109,7 +109,7 @@ public MemoryPool(NeoSystem system, int capacity) Capacity = capacity; LoadMaxTxLimitsFromPolicyPlugins(); IConsensusContext context = new ConsensusContext(null); - context.LoadContextFromStore(system.store); + context.LoadContextFromStore(system.consensusStore); if (context.Transactions == null) return; foreach (var tx in context.Transactions.Values) { diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index fcd7d53876..773dc140bb 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -27,12 +27,14 @@ public class NeoSystem : IDisposable public RpcServer RpcServer { get; private set; } internal readonly Store store; + internal readonly Store consensusStore; private Peer.Start start_message = null; private bool suspend = false; - public NeoSystem(Store store) + public NeoSystem(Store store, Store consensusStore = null) { this.store = store; + this.consensusStore = consensusStore ?? store; this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); @@ -56,9 +58,9 @@ internal void ResumeNodeStartup() } } - public void StartConsensus(Wallet wallet, Store consensus_store = null) + public void StartConsensus(Wallet wallet) { - Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, consensus_store ?? store, wallet)); + Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, consensusStore, wallet)); Consensus.Tell(new ConsensusService.Start()); } diff --git a/neo/Persistence/LevelDB/Prefixes.cs b/neo/Persistence/LevelDB/Prefixes.cs index 75ffd1fddc..9c641796ce 100644 --- a/neo/Persistence/LevelDB/Prefixes.cs +++ b/neo/Persistence/LevelDB/Prefixes.cs @@ -19,6 +19,6 @@ internal static class Prefixes public const byte IX_CurrentHeader = 0xc1; public const byte SYS_Version = 0xf0; - public const byte CN_CONTEXT = 0xf4; + public const byte CN_Context = 0xf4; } } From 411f905d5df3bd5b0c4fe663a5c5e219a350575f Mon Sep 17 00:00:00 2001 From: jsolman Date: Mon, 28 Jan 2019 19:17:44 -0800 Subject: [PATCH 07/34] Add one extra check when restoring transactions from saved consensus state. --- neo/Ledger/MemoryPool.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index a74fcdd5ba..fd26b689f9 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -113,6 +113,7 @@ public MemoryPool(NeoSystem system, int capacity) if (context.Transactions == null) return; foreach (var tx in context.Transactions.Values) { + if (system.store.ContainsTransaction(tx.Hash)) continue; TryAdd(tx.Hash, tx); } } From f43a5ed3791bd41937b8797d43c0f98463f3dc64 Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 29 Jan 2019 09:15:02 -0800 Subject: [PATCH 08/34] Move loading transactions from the last saved ConsensusContext out of MemoryPool. --- neo/Ledger/Blockchain.cs | 13 ++++++++++++- neo/Ledger/MemoryPool.cs | 9 --------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 82dac883a6..99fc6fe6c4 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using Neo.Consensus; namespace Neo.Ledger { @@ -148,8 +149,18 @@ static Blockchain() public Blockchain(NeoSystem system, Store store) { this.system = system; - this.MemPool = new MemoryPool(system, MemoryPoolMaxTransactions); this.Store = store; + this.MemPool = new MemoryPool(system, MemoryPoolMaxTransactions); + + IConsensusContext context = new ConsensusContext(null); + context.LoadContextFromStore(system.consensusStore); + if (context.Transactions == null) return; + foreach (var tx in context.Transactions.Values) + { + if (store.ContainsTransaction(tx.Hash)) continue; + MemPool.TryAdd(tx.Hash, tx); + } + lock (lockObj) { if (singleton != null) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index fd26b689f9..0d857495de 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -6,7 +6,6 @@ using System.Runtime.CompilerServices; using System.Threading; using Akka.Util.Internal; -using Neo.Consensus; using Neo.Network.P2P; using Neo.Persistence; using Neo.Plugins; @@ -108,14 +107,6 @@ public MemoryPool(NeoSystem system, int capacity) _system = system; Capacity = capacity; LoadMaxTxLimitsFromPolicyPlugins(); - IConsensusContext context = new ConsensusContext(null); - context.LoadContextFromStore(system.consensusStore); - if (context.Transactions == null) return; - foreach (var tx in context.Transactions.Values) - { - if (system.store.ContainsTransaction(tx.Hash)) continue; - TryAdd(tx.Hash, tx); - } } public void LoadMaxTxLimitsFromPolicyPlugins() From 28f05ebdea2ebb5c6240b7e69dc7582e98e2d64b Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 29 Jan 2019 09:33:46 -0800 Subject: [PATCH 09/34] More clean-up. --- neo/Consensus/Helper.cs | 13 +++++++++++++ neo/Ledger/Blockchain.cs | 11 +---------- neo/NeoSystem.cs | 8 +++----- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index b806fcc697..195ea4e678 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -1,5 +1,6 @@ using System.IO; using Neo.IO; +using Neo.Ledger; using Neo.Persistence; using Neo.Persistence.LevelDB; @@ -24,5 +25,17 @@ internal static void LoadContextFromStore(this IConsensusContext context, Store } } } + + internal static void LoadTransactionsToMemoryPoolFromSavedConsensusContext(MemoryPool memoryPool, Store store, Store consensusStore) + { + IConsensusContext context = new ConsensusContext(null); + context.LoadContextFromStore(consensusStore); + if (context.Transactions == null) return; + foreach (var tx in context.Transactions.Values) + { + if (store.ContainsTransaction(tx.Hash)) continue; + memoryPool.TryAdd(tx.Hash, tx); + } + } } } \ No newline at end of file diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 99fc6fe6c4..5aef520dbb 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -14,7 +14,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using Neo.Consensus; namespace Neo.Ledger { @@ -151,15 +150,7 @@ public Blockchain(NeoSystem system, Store store) this.system = system; this.Store = store; this.MemPool = new MemoryPool(system, MemoryPoolMaxTransactions); - - IConsensusContext context = new ConsensusContext(null); - context.LoadContextFromStore(system.consensusStore); - if (context.Transactions == null) return; - foreach (var tx in context.Transactions.Values) - { - if (store.ContainsTransaction(tx.Hash)) continue; - MemPool.TryAdd(tx.Hash, tx); - } + Consensus.Helper.LoadTransactionsToMemoryPoolFromSavedConsensusContext(MemPool, store, system.ConsensusStore); lock (lockObj) { diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index 773dc140bb..1dc3eb5d64 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -26,15 +26,13 @@ public class NeoSystem : IDisposable public IActorRef Consensus { get; private set; } public RpcServer RpcServer { get; private set; } - internal readonly Store store; - internal readonly Store consensusStore; + internal readonly Store ConsensusStore; private Peer.Start start_message = null; private bool suspend = false; public NeoSystem(Store store, Store consensusStore = null) { - this.store = store; - this.consensusStore = consensusStore ?? store; + this.ConsensusStore = consensusStore ?? store; this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); @@ -60,7 +58,7 @@ internal void ResumeNodeStartup() public void StartConsensus(Wallet wallet) { - Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, consensusStore, wallet)); + Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, ConsensusStore, wallet)); Consensus.Tell(new ConsensusService.Start()); } From 91f31de1e5df0dc8e1e247ffd7c8323f878256b9 Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 29 Jan 2019 10:20:01 -0800 Subject: [PATCH 10/34] Further decoupling, don't expose the consensus store from NeoSystem. --- neo/Ledger/Blockchain.cs | 8 ++++---- neo/NeoSystem.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 5aef520dbb..8c7d97a777 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -145,12 +145,12 @@ static Blockchain() GenesisBlock.RebuildMerkleRoot(); } - public Blockchain(NeoSystem system, Store store) + public Blockchain(NeoSystem system, Store store, Store consensusStore) { this.system = system; this.Store = store; this.MemPool = new MemoryPool(system, MemoryPoolMaxTransactions); - Consensus.Helper.LoadTransactionsToMemoryPoolFromSavedConsensusContext(MemPool, store, system.ConsensusStore); + Consensus.Helper.LoadTransactionsToMemoryPoolFromSavedConsensusContext(MemPool, store, consensusStore); lock (lockObj) { @@ -685,9 +685,9 @@ internal static void ProcessValidatorStateDescriptor(StateDescriptor descriptor, } } - public static Props Props(NeoSystem system, Store store) + public static Props Props(NeoSystem system, Store store, Store consensusStore) { - return Akka.Actor.Props.Create(() => new Blockchain(system, store)).WithMailbox("blockchain-mailbox"); + return Akka.Actor.Props.Create(() => new Blockchain(system, store, consensusStore)).WithMailbox("blockchain-mailbox"); } private void SaveHeaderHashList(Snapshot snapshot = null) diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index 1dc3eb5d64..c8dd59e3ae 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -26,14 +26,14 @@ public class NeoSystem : IDisposable public IActorRef Consensus { get; private set; } public RpcServer RpcServer { get; private set; } - internal readonly Store ConsensusStore; + private readonly Store consensusStore; private Peer.Start start_message = null; private bool suspend = false; public NeoSystem(Store store, Store consensusStore = null) { - this.ConsensusStore = consensusStore ?? store; - this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store)); + this.consensusStore = consensusStore ?? store; + this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store, this.consensusStore)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); Plugin.LoadPlugins(this); @@ -58,7 +58,7 @@ internal void ResumeNodeStartup() public void StartConsensus(Wallet wallet) { - Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, ConsensusStore, wallet)); + Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, consensusStore, wallet)); Consensus.Tell(new ConsensusService.Start()); } From eedeffba9702afd7f2e377e19c925b512bf718db Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 29 Jan 2019 21:00:05 -0800 Subject: [PATCH 11/34] Handle saving and restoring consensus conetext when RequestSent, RequestReceived, or ResponseSent. --- neo/Consensus/ConsensusService.cs | 104 ++++++++++++++++++++---------- neo/Consensus/Helper.cs | 4 +- 2 files changed, 74 insertions(+), 34 deletions(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 86b3980957..aed555aaf9 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -65,6 +65,7 @@ private bool AddTransaction(Transaction tx, bool verify) Log($"send prepare response"); context.State |= ConsensusState.ResponseSent; context.Preparations[context.MyIndex] = context.Preparations[context.PrimaryIndex]; + context.WriteContextToStore(store); localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareResponse(context.Preparations[context.MyIndex]) }); CheckPreparations(); } @@ -104,8 +105,10 @@ private void CheckExpectedView(byte view_number) if (context.ExpectedView.Count(p => p == view_number) >= context.M) { InitializeConsensus(view_number); - // Save our view so if we crash and come back we will be closer to the correct view. - context.WriteContextToStore(store); + // TODO: Can save here also, once we have the regeneration message, and if we restart without + // TODO: ConsensusState.RequestSent or ConsensusState.ResponseSent set then we can send change view + // TODO: again to potentially receive the regeneration message. + // context.WriteContextToStore(store); } } @@ -227,6 +230,37 @@ private void OnPersistCompleted(Block block) InitializeConsensus(0); } + private void ObtainTransactionsForConsensus(Transaction minerTransaction) + { + Dictionary mempoolVerified = Blockchain.Singleton.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash); + List unverified = new List(); + foreach (UInt256 hash in context.TransactionHashes.Skip(1)) + { + 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 (!AddTransaction(minerTransaction, 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 OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest message) { if (context.State.HasFlag(ConsensusState.RequestReceived)) return; @@ -259,34 +293,10 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m if (context.Commits[i] != null) if (!Crypto.Default.VerifySignature(hashData, context.Commits[i], context.Validators[i].EncodePoint(false))) context.Commits[i] = null; - Dictionary mempoolVerified = Blockchain.Singleton.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash); - List unverified = new List(); - foreach (UInt256 hash in context.TransactionHashes.Skip(1)) - { - 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 (!AddTransaction(message.MinerTransaction, 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) - }); - } + // Save our view so if we crash and come back we will be on the last view that received the PrepareRequest + context.WriteContextToStore(store); + ObtainTransactionsForConsensus(message.MinerTransaction); } private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse message) @@ -336,11 +346,37 @@ private void OnStart() { Log("OnStart"); started = true; - context.LoadContextFromStore(store); + bool loadedState = context.LoadContextFromStore(store); + if (!loadedState || Blockchain.Singleton.Height >= context.BlockIndex) + { + InitializeConsensus(0); + return; + } + if (context.State.HasFlag(ConsensusState.CommitSent)) CheckPreparations(); else - InitializeConsensus(context.ViewNumber); + { + if (context.State.HasFlag(ConsensusState.RequestSent)) + { + // Note: The code that starts consensus should wait till some peers are connected to start consensus. + localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareRequest() }); + } + else if (context.State.HasFlag(ConsensusState.ResponseSent)) + { + localNode.Tell(new LocalNode.SendDirectly + { + Inventory = context.MakePrepareResponse(context.Preparations[context.MyIndex]) + }); + } + else if (context.State.HasFlag(ConsensusState.RequestReceived)) + { + ObtainTransactionsForConsensus(context.Transactions[context.TransactionHashes[0]]); + } + // TODO: else Request change view to the current view in order to receive regeneration + + ChangeTimer(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (context.ViewNumber + 1))); + } } private void OnTimer(Timer timer) @@ -353,9 +389,11 @@ private void OnTimer(Timer timer) Log($"send prepare request: height={timer.Height} view={timer.ViewNumber}"); context.Fill(); ConsensusPayload request = context.MakePrepareRequest(); - localNode.Tell(new LocalNode.SendDirectly { Inventory = request }); - context.State |= ConsensusState.RequestSent; context.Preparations[context.MyIndex] = request.Hash; + context.State |= ConsensusState.RequestSent; + context.WriteContextToStore(store); + localNode.Tell(new LocalNode.SendDirectly { Inventory = request }); + if (context.TransactionHashes.Length > 1) { foreach (InvPayload payload in InvPayload.CreateGroup(InventoryType.TX, context.TransactionHashes.Skip(1).ToArray())) diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index 195ea4e678..e4bb72047c 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -13,7 +13,7 @@ internal static void WriteContextToStore(this IConsensusContext context, Store s store.Put(Prefixes.CN_Context, new byte[0], context.ToArray()); } - internal static void LoadContextFromStore(this IConsensusContext context, Store store) + internal static bool LoadContextFromStore(this IConsensusContext context, Store store) { byte[] data = store.Get(Prefixes.CN_Context, new byte[0]); if (data != null) @@ -22,8 +22,10 @@ internal static void LoadContextFromStore(this IConsensusContext context, Store using (BinaryReader reader = new BinaryReader(ms)) { context.Deserialize(reader); + return true; } } + return false; } internal static void LoadTransactionsToMemoryPoolFromSavedConsensusContext(MemoryPool memoryPool, Store store, Store consensusStore) From 8eaf669789b57e5bb163a960abd97bfe5f1f5bdf Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 29 Jan 2019 23:24:52 -0800 Subject: [PATCH 12/34] Make unit tests pass again. Consensus unit tests still need attention though. --- neo.UnitTests/UT_Consensus.cs | 2 ++ neo/Consensus/ConsensusService.cs | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/neo.UnitTests/UT_Consensus.cs b/neo.UnitTests/UT_Consensus.cs index 6010621f27..dd620981d6 100644 --- a/neo.UnitTests/UT_Consensus.cs +++ b/neo.UnitTests/UT_Consensus.cs @@ -14,6 +14,7 @@ using System.Numerics; using System.Security.Cryptography; using Neo.Persistence; +using Neo.Persistence.LevelDB; using ECPoint = Neo.Cryptography.ECC.ECPoint; namespace Neo.UnitTests @@ -35,6 +36,7 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() var mockConsensusContext = new Mock(); var mockStore = new Mock(); + mockStore.Setup(p => p.Get(Prefixes.CN_Context, It.IsAny())).Returns((byte[]) null); // context.Reset(): do nothing //mockConsensusContext.Setup(mr => mr.Reset()).Verifiable(); // void diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index aed555aaf9..3d58903af9 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -347,6 +347,7 @@ private void OnStart() Log("OnStart"); started = true; bool loadedState = context.LoadContextFromStore(store); + Console.WriteLine($"loaded state as {loadedState}"); if (!loadedState || Blockchain.Singleton.Height >= context.BlockIndex) { InitializeConsensus(0); @@ -389,10 +390,12 @@ private void OnTimer(Timer timer) Log($"send prepare request: height={timer.Height} view={timer.ViewNumber}"); context.Fill(); ConsensusPayload request = context.MakePrepareRequest(); + // TODO: fix the unit tests; would like to move this line to after line 397, but tests have an issue with line 395. + localNode.Tell(new LocalNode.SendDirectly { Inventory = request }); context.Preparations[context.MyIndex] = request.Hash; context.State |= ConsensusState.RequestSent; context.WriteContextToStore(store); - localNode.Tell(new LocalNode.SendDirectly { Inventory = request }); + if (context.TransactionHashes.Length > 1) { From 240c5bb3da8fac58b26f02d8caff622fbfbbaf2f Mon Sep 17 00:00:00 2001 From: jsolman Date: Wed, 30 Jan 2019 09:39:59 -0800 Subject: [PATCH 13/34] Don't write the consensus state after resposne sent to reduce writes. --- neo/Consensus/ConsensusService.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 3d58903af9..8e1eef83cb 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -65,7 +65,8 @@ private bool AddTransaction(Transaction tx, bool verify) Log($"send prepare response"); context.State |= ConsensusState.ResponseSent; context.Preparations[context.MyIndex] = context.Preparations[context.PrimaryIndex]; - context.WriteContextToStore(store); + // For performance reasons, we won't write the context again here. + // context.WriteContextToStore(store); localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareResponse(context.Preparations[context.MyIndex]) }); CheckPreparations(); } @@ -363,6 +364,8 @@ private void OnStart() // Note: The code that starts consensus should wait till some peers are connected to start consensus. localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareRequest() }); } + /* + // Note: We don't write the state out at ResponseSent in order to reduce writes. else if (context.State.HasFlag(ConsensusState.ResponseSent)) { localNode.Tell(new LocalNode.SendDirectly @@ -370,6 +373,7 @@ private void OnStart() Inventory = context.MakePrepareResponse(context.Preparations[context.MyIndex]) }); } + */ else if (context.State.HasFlag(ConsensusState.RequestReceived)) { ObtainTransactionsForConsensus(context.Transactions[context.TransactionHashes[0]]); From 92e6987811d48b519c78c26b22ebf235eea34abd Mon Sep 17 00:00:00 2001 From: jsolman Date: Wed, 30 Jan 2019 22:35:29 -0800 Subject: [PATCH 14/34] Remove unneeded commented code. --- neo/Consensus/ConsensusService.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 8e1eef83cb..6b7c4fa039 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -65,8 +65,6 @@ private bool AddTransaction(Transaction tx, bool verify) Log($"send prepare response"); context.State |= ConsensusState.ResponseSent; context.Preparations[context.MyIndex] = context.Preparations[context.PrimaryIndex]; - // For performance reasons, we won't write the context again here. - // context.WriteContextToStore(store); localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareResponse(context.Preparations[context.MyIndex]) }); CheckPreparations(); } @@ -106,10 +104,6 @@ private void CheckExpectedView(byte view_number) if (context.ExpectedView.Count(p => p == view_number) >= context.M) { InitializeConsensus(view_number); - // TODO: Can save here also, once we have the regeneration message, and if we restart without - // TODO: ConsensusState.RequestSent or ConsensusState.ResponseSent set then we can send change view - // TODO: again to potentially receive the regeneration message. - // context.WriteContextToStore(store); } } @@ -364,21 +358,11 @@ private void OnStart() // Note: The code that starts consensus should wait till some peers are connected to start consensus. localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareRequest() }); } - /* - // Note: We don't write the state out at ResponseSent in order to reduce writes. - else if (context.State.HasFlag(ConsensusState.ResponseSent)) - { - localNode.Tell(new LocalNode.SendDirectly - { - Inventory = context.MakePrepareResponse(context.Preparations[context.MyIndex]) - }); - } - */ else if (context.State.HasFlag(ConsensusState.RequestReceived)) { ObtainTransactionsForConsensus(context.Transactions[context.TransactionHashes[0]]); } - // TODO: else Request change view to the current view in order to receive regeneration + // TODO: Request change view to the current view in order to receive regeneration ChangeTimer(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (context.ViewNumber + 1))); } From 3b064740015174910872a3316e45d7f9e7d9d57a Mon Sep 17 00:00:00 2001 From: jsolman Date: Thu, 31 Jan 2019 00:09:05 -0800 Subject: [PATCH 15/34] Fix saving context to include the miner transaction after receiving the PrepareRequest. --- neo/Consensus/ConsensusService.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 6b7c4fa039..da07b09c0e 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -225,7 +225,7 @@ private void OnPersistCompleted(Block block) InitializeConsensus(0); } - private void ObtainTransactionsForConsensus(Transaction minerTransaction) + private void ObtainTransactionsForConsensus() { Dictionary mempoolVerified = Blockchain.Singleton.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash); List unverified = new List(); @@ -245,7 +245,6 @@ private void ObtainTransactionsForConsensus(Transaction minerTransaction) foreach (Transaction tx in unverified) if (!AddTransaction(tx, true)) return; - if (!AddTransaction(minerTransaction, true)) return; if (context.Transactions.Count < context.TransactionHashes.Length) { UInt256[] hashes = context.TransactionHashes.Where(i => !context.Transactions.ContainsKey(i)).ToArray(); @@ -289,9 +288,11 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m if (!Crypto.Default.VerifySignature(hashData, context.Commits[i], context.Validators[i].EncodePoint(false))) context.Commits[i] = null; + if (!AddTransaction(message.MinerTransaction, true)) return; + // Save our view so if we crash and come back we will be on the last view that received the PrepareRequest context.WriteContextToStore(store); - ObtainTransactionsForConsensus(message.MinerTransaction); + ObtainTransactionsForConsensus(); } private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse message) @@ -360,7 +361,9 @@ private void OnStart() } else if (context.State.HasFlag(ConsensusState.RequestReceived)) { - ObtainTransactionsForConsensus(context.Transactions[context.TransactionHashes[0]]); + var minerTransaction = context.Transactions[context.TransactionHashes[0]]; + if (AddTransaction(minerTransaction, true)) + ObtainTransactionsForConsensus(); } // TODO: Request change view to the current view in order to receive regeneration From 323384c90fc9a0066375b5148ddfef9800689d8d Mon Sep 17 00:00:00 2001 From: jsolman Date: Thu, 31 Jan 2019 01:40:42 -0800 Subject: [PATCH 16/34] Ensure consensus context has snapshot and keypair set upon loading the context from the store. --- neo/Consensus/Helper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index e4bb72047c..4e00d8326c 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -18,6 +18,7 @@ internal static bool LoadContextFromStore(this IConsensusContext context, Store byte[] data = store.Get(Prefixes.CN_Context, new byte[0]); if (data != null) { + context.Reset(0); using (MemoryStream ms = new MemoryStream(data, false)) using (BinaryReader reader = new BinaryReader(ms)) { @@ -40,4 +41,4 @@ internal static void LoadTransactionsToMemoryPoolFromSavedConsensusContext(Memor } } } -} \ No newline at end of file +} From d312b2c95674a2eea8845b3f10139e19b168e54d Mon Sep 17 00:00:00 2001 From: jsolman Date: Thu, 31 Jan 2019 02:59:30 -0800 Subject: [PATCH 17/34] Fix loading transactions into the MemoryPool from consensus context. --- neo/Consensus/Helper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index 4e00d8326c..d98e4e002f 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -13,12 +13,12 @@ internal static void WriteContextToStore(this IConsensusContext context, Store s store.Put(Prefixes.CN_Context, new byte[0], context.ToArray()); } - internal static bool LoadContextFromStore(this IConsensusContext context, Store store) + internal static bool LoadContextFromStore(this IConsensusContext context, Store store, bool shouldReset=true) { byte[] data = store.Get(Prefixes.CN_Context, new byte[0]); if (data != null) { - context.Reset(0); + if (shouldReset) context.Reset(0); using (MemoryStream ms = new MemoryStream(data, false)) using (BinaryReader reader = new BinaryReader(ms)) { @@ -32,7 +32,7 @@ internal static bool LoadContextFromStore(this IConsensusContext context, Store internal static void LoadTransactionsToMemoryPoolFromSavedConsensusContext(MemoryPool memoryPool, Store store, Store consensusStore) { IConsensusContext context = new ConsensusContext(null); - context.LoadContextFromStore(consensusStore); + context.LoadContextFromStore(consensusStore, false); if (context.Transactions == null) return; foreach (var tx in context.Transactions.Values) { From 6c33b6209a7ce9f29a3e3afab2dafe2dfc109546 Mon Sep 17 00:00:00 2001 From: jsolman Date: Thu, 31 Jan 2019 15:16:32 -0800 Subject: [PATCH 18/34] More precise check when restoring consensus context. --- neo/Consensus/ConsensusService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index da07b09c0e..c364e4142b 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -344,7 +344,7 @@ private void OnStart() started = true; bool loadedState = context.LoadContextFromStore(store); Console.WriteLine($"loaded state as {loadedState}"); - if (!loadedState || Blockchain.Singleton.Height >= context.BlockIndex) + if (!loadedState || Blockchain.Singleton.Height + 1 != context.BlockIndex) { InitializeConsensus(0); return; From 1c744ea1da2ab0ce987d8c76df1766cd1b06fb8b Mon Sep 17 00:00:00 2001 From: jsolman Date: Mon, 4 Feb 2019 03:04:17 -0800 Subject: [PATCH 19/34] Don't need to save consensus state on request sent or received since we will add a Recovery message to restore the state. --- neo/Consensus/ConsensusService.cs | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index c364e4142b..27a9903d3b 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -290,8 +290,6 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m if (!AddTransaction(message.MinerTransaction, true)) return; - // Save our view so if we crash and come back we will be on the last view that received the PrepareRequest - context.WriteContextToStore(store); ObtainTransactionsForConsensus(); } @@ -343,32 +341,13 @@ private void OnStart() Log("OnStart"); started = true; bool loadedState = context.LoadContextFromStore(store); - Console.WriteLine($"loaded state as {loadedState}"); - if (!loadedState || Blockchain.Singleton.Height + 1 != context.BlockIndex) + if (loadedState && context.State.HasFlag(ConsensusState.CommitSent) && Blockchain.Singleton.Height + 1 == context.BlockIndex) { - InitializeConsensus(0); + CheckPreparations(); return; } - if (context.State.HasFlag(ConsensusState.CommitSent)) - CheckPreparations(); - else - { - if (context.State.HasFlag(ConsensusState.RequestSent)) - { - // Note: The code that starts consensus should wait till some peers are connected to start consensus. - localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareRequest() }); - } - else if (context.State.HasFlag(ConsensusState.RequestReceived)) - { - var minerTransaction = context.Transactions[context.TransactionHashes[0]]; - if (AddTransaction(minerTransaction, true)) - ObtainTransactionsForConsensus(); - } - // TODO: Request change view to the current view in order to receive regeneration - - ChangeTimer(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (context.ViewNumber + 1))); - } + InitializeConsensus(0); } private void OnTimer(Timer timer) @@ -381,12 +360,9 @@ private void OnTimer(Timer timer) Log($"send prepare request: height={timer.Height} view={timer.ViewNumber}"); context.Fill(); ConsensusPayload request = context.MakePrepareRequest(); - // TODO: fix the unit tests; would like to move this line to after line 397, but tests have an issue with line 395. localNode.Tell(new LocalNode.SendDirectly { Inventory = request }); context.Preparations[context.MyIndex] = request.Hash; context.State |= ConsensusState.RequestSent; - context.WriteContextToStore(store); - if (context.TransactionHashes.Length > 1) { From 700a0aa11d75530704232dd9aaea8b4ba483a8ed Mon Sep 17 00:00:00 2001 From: jsolman Date: Sat, 16 Feb 2019 00:36:29 -0800 Subject: [PATCH 20/34] Fix test compile. --- neo.UnitTests/UT_Consensus.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/neo.UnitTests/UT_Consensus.cs b/neo.UnitTests/UT_Consensus.cs index cc998a5126..b81cf7e8f1 100644 --- a/neo.UnitTests/UT_Consensus.cs +++ b/neo.UnitTests/UT_Consensus.cs @@ -15,6 +15,7 @@ using System.Linq; using System.Numerics; using System.Security.Cryptography; +using Neo.Persistence.LevelDB; using ECPoint = Neo.Cryptography.ECC.ECPoint; namespace Neo.UnitTests From 62d5e5a8c2abec386d4204456c94be92e978c42c Mon Sep 17 00:00:00 2001 From: jsolman Date: Sat, 16 Feb 2019 00:53:25 -0800 Subject: [PATCH 21/34] Fix tests. --- neo.UnitTests/TestBlockchain.cs | 2 ++ neo.UnitTests/UT_Consensus.cs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/neo.UnitTests/TestBlockchain.cs b/neo.UnitTests/TestBlockchain.cs index b456dc820b..94caa6be4d 100644 --- a/neo.UnitTests/TestBlockchain.cs +++ b/neo.UnitTests/TestBlockchain.cs @@ -4,6 +4,7 @@ using Neo.Ledger; using Neo.Persistence; using System; +using Neo.Persistence.LevelDB; namespace Neo.UnitTests { @@ -32,6 +33,7 @@ public static NeoSystem InitializeMockNeoSystem() mockSnapshot.SetupGet(p => p.HeaderHashIndex).Returns(new TestMetaDataCache()); var mockStore = new Mock(); + mockStore.Setup(p => p.Get(Prefixes.CN_Context, It.IsAny())).Returns((byte[]) null); var defaultTx = TestUtils.CreateRandomHashInvocationMockTransaction().Object; mockStore.Setup(p => p.GetBlocks()).Returns(new TestDataCache()); diff --git a/neo.UnitTests/UT_Consensus.cs b/neo.UnitTests/UT_Consensus.cs index b81cf7e8f1..f03ba98bca 100644 --- a/neo.UnitTests/UT_Consensus.cs +++ b/neo.UnitTests/UT_Consensus.cs @@ -43,7 +43,6 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() var mockConsensusContext = new Mock(); var mockStore = new Mock(); - mockStore.Setup(p => p.Get(Prefixes.CN_Context, It.IsAny())).Returns((byte[]) null); // context.Reset(): do nothing //mockConsensusContext.Setup(mr => mr.Reset()).Verifiable(); // void From 5e29fdccfe865f5112651b5b369837605fa00f35 Mon Sep 17 00:00:00 2001 From: jsolman Date: Sat, 16 Feb 2019 10:59:40 -0800 Subject: [PATCH 22/34] Don't separate the code for obtaining transactions into a separte method. --- neo/Consensus/ConsensusService.cs | 60 ++++++++++++++----------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index e0399b8955..fd9348c6b3 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -269,38 +269,6 @@ private void OnPersistCompleted(Block block) InitializeConsensus(0); } - private void ObtainTransactionsForConsensus() - { - Dictionary mempoolVerified = - Blockchain.Singleton.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash); - List unverified = new List(); - foreach (UInt256 hash in context.TransactionHashes.Skip(1)) - { - 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 OnRecoveryMessageReceived(ConsensusPayload payload, RecoveryMessage message) { if (message.ViewNumber < context.ViewNumber) return; @@ -363,10 +331,34 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m if (context.CommitPayloads[i] != null) if (!Crypto.Default.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage().Signature, context.Validators[i].EncodePoint(false))) context.CommitPayloads[i] = null; + Dictionary mempoolVerified = Blockchain.Singleton.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash); + List unverified = new List(); + foreach (UInt256 hash in context.TransactionHashes.Skip(1)) + { + 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 (!AddTransaction(message.MinerTransaction, true)) return; - - ObtainTransactionsForConsensus(); + 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(ConsensusPayload payload, PrepareResponse message) From f955db53e63bb7912733fdfb1fde291207bcc9df Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 19 Feb 2019 11:35:46 -0800 Subject: [PATCH 23/34] Write consensus state synchronously upon commit. Move constant for consensus state LevelDB prefix. --- neo.UnitTests/TestBlockchain.cs | 3 +-- neo/Consensus/Helper.cs | 12 ++++++++++-- neo/Persistence/LevelDB/LevelDBStore.cs | 4 +++- neo/Persistence/LevelDB/Prefixes.cs | 6 +++++- neo/Persistence/Store.cs | 3 ++- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/neo.UnitTests/TestBlockchain.cs b/neo.UnitTests/TestBlockchain.cs index 94caa6be4d..175a6ebfba 100644 --- a/neo.UnitTests/TestBlockchain.cs +++ b/neo.UnitTests/TestBlockchain.cs @@ -4,7 +4,6 @@ using Neo.Ledger; using Neo.Persistence; using System; -using Neo.Persistence.LevelDB; namespace Neo.UnitTests { @@ -33,7 +32,7 @@ public static NeoSystem InitializeMockNeoSystem() mockSnapshot.SetupGet(p => p.HeaderHashIndex).Returns(new TestMetaDataCache()); var mockStore = new Mock(); - mockStore.Setup(p => p.Get(Prefixes.CN_Context, It.IsAny())).Returns((byte[]) null); + mockStore.Setup(p => p.Get(Consensus.Helper.CN_Context, It.IsAny())).Returns((byte[]) null); var defaultTx = TestUtils.CreateRandomHashInvocationMockTransaction().Object; mockStore.Setup(p => p.GetBlocks()).Returns(new TestDataCache()); diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index d98e4e002f..c554bb8bc1 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -1,5 +1,6 @@ using System.IO; using Neo.IO; +using Neo.IO.Data.LevelDB; using Neo.Ledger; using Neo.Persistence; using Neo.Persistence.LevelDB; @@ -8,14 +9,21 @@ namespace Neo.Consensus { public static class Helper { + /// + /// Prefix for saving consensus state. + /// + public const byte CN_Context = 0xf4; + + private static readonly WriteOptions SynchronousWriteOptions = new WriteOptions { Sync = true }; + internal static void WriteContextToStore(this IConsensusContext context, Store store) { - store.Put(Prefixes.CN_Context, new byte[0], context.ToArray()); + store.Put(CN_Context, new byte[0], context.ToArray(), SynchronousWriteOptions); } internal static bool LoadContextFromStore(this IConsensusContext context, Store store, bool shouldReset=true) { - byte[] data = store.Get(Prefixes.CN_Context, new byte[0]); + byte[] data = store.Get(CN_Context, new byte[0]); if (data != null) { if (shouldReset) context.Reset(0); diff --git a/neo/Persistence/LevelDB/LevelDBStore.cs b/neo/Persistence/LevelDB/LevelDBStore.cs index c4cb97437e..775d509da2 100644 --- a/neo/Persistence/LevelDB/LevelDBStore.cs +++ b/neo/Persistence/LevelDB/LevelDBStore.cs @@ -112,8 +112,10 @@ public override MetaDataCache GetHeaderHashIndex() return new DbMetaDataCache(db, null, null, Prefixes.IX_CurrentHeader); } - public override void Put(byte prefix, byte[] key, byte[] value) + public override void Put(byte prefix, byte[] key, byte[] value, WriteOptions writeOptions = null) { + if (writeOptions == null) + writeOptions = WriteOptions.Default; db.Put(WriteOptions.Default, SliceBuilder.Begin(prefix).Add(key), value); } } diff --git a/neo/Persistence/LevelDB/Prefixes.cs b/neo/Persistence/LevelDB/Prefixes.cs index 9c641796ce..5f7e3ea90b 100644 --- a/neo/Persistence/LevelDB/Prefixes.cs +++ b/neo/Persistence/LevelDB/Prefixes.cs @@ -19,6 +19,10 @@ internal static class Prefixes public const byte IX_CurrentHeader = 0xc1; public const byte SYS_Version = 0xf0; - public const byte CN_Context = 0xf4; + + /* Prefixes 0xf1 to 0xff are reserved for external use. + * + * Note: The saved consensus state uses the Prefix 0xf4 + */ } } diff --git a/neo/Persistence/Store.cs b/neo/Persistence/Store.cs index ea1e1e705c..e9e8035f2e 100644 --- a/neo/Persistence/Store.cs +++ b/neo/Persistence/Store.cs @@ -1,5 +1,6 @@ using Neo.Cryptography.ECC; using Neo.IO.Caching; +using Neo.IO.Data.LevelDB; using Neo.IO.Wrappers; using Neo.Ledger; @@ -35,7 +36,7 @@ public abstract class Store : IPersistence public abstract MetaDataCache GetValidatorsCount(); public abstract MetaDataCache GetBlockHashIndex(); public abstract MetaDataCache GetHeaderHashIndex(); - public abstract void Put(byte prefix, byte[] key, byte[] value); + public abstract void Put(byte prefix, byte[] key, byte[] value, WriteOptions writeOptions = null); public abstract Snapshot GetSnapshot(); } From 8aeaf801e6b91c40c547c812cba65f1c51541181 Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 19 Feb 2019 11:41:04 -0800 Subject: [PATCH 24/34] Use the passed writeOptions for Put to the store. --- neo/Persistence/LevelDB/LevelDBStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/Persistence/LevelDB/LevelDBStore.cs b/neo/Persistence/LevelDB/LevelDBStore.cs index 775d509da2..5374819d21 100644 --- a/neo/Persistence/LevelDB/LevelDBStore.cs +++ b/neo/Persistence/LevelDB/LevelDBStore.cs @@ -116,7 +116,7 @@ public override void Put(byte prefix, byte[] key, byte[] value, WriteOptions wri { if (writeOptions == null) writeOptions = WriteOptions.Default; - db.Put(WriteOptions.Default, SliceBuilder.Begin(prefix).Add(key), value); + db.Put(writeOptions, SliceBuilder.Begin(prefix).Add(key), value); } } } From 82a0f2b63719da15f1d2c382d204f57fe3744694 Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 19 Feb 2019 12:00:54 -0800 Subject: [PATCH 25/34] Decouple restoring memory pool from the consensus state from the Blockchain subsystem. --- neo/Consensus/Helper.cs | 14 ++++++-------- neo/Ledger/Blockchain.cs | 7 +++---- neo/NeoSystem.cs | 10 +++++++++- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index c554bb8bc1..6b8539a187 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -1,7 +1,10 @@ -using System.IO; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; using Neo.IO; using Neo.IO.Data.LevelDB; using Neo.Ledger; +using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.Persistence.LevelDB; @@ -37,16 +40,11 @@ internal static bool LoadContextFromStore(this IConsensusContext context, Store return false; } - internal static void LoadTransactionsToMemoryPoolFromSavedConsensusContext(MemoryPool memoryPool, Store store, Store consensusStore) + internal static IEnumerable RetreiveTransactionsFromSavedConsensusContext(MemoryPool memoryPool, Store store, Store consensusStore) { IConsensusContext context = new ConsensusContext(null); context.LoadContextFromStore(consensusStore, false); - if (context.Transactions == null) return; - foreach (var tx in context.Transactions.Values) - { - if (store.ContainsTransaction(tx.Hash)) continue; - memoryPool.TryAdd(tx.Hash, tx); - } + return context.Transactions?.Values; } } } diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index c74d689711..9e69661292 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -145,12 +145,11 @@ static Blockchain() GenesisBlock.RebuildMerkleRoot(); } - public Blockchain(NeoSystem system, Store store, Store consensusStore) + public Blockchain(NeoSystem system, Store store) { this.system = system; this.Store = store; this.MemPool = new MemoryPool(system, MemoryPoolMaxTransactions); - Consensus.Helper.LoadTransactionsToMemoryPoolFromSavedConsensusContext(MemPool, store, consensusStore); lock (lockObj) { @@ -685,9 +684,9 @@ internal static void ProcessValidatorStateDescriptor(StateDescriptor descriptor, } } - public static Props Props(NeoSystem system, Store store, Store consensusStore) + public static Props Props(NeoSystem system, Store store) { - return Akka.Actor.Props.Create(() => new Blockchain(system, store, consensusStore)).WithMailbox("blockchain-mailbox"); + return Akka.Actor.Props.Create(() => new Blockchain(system, store)).WithMailbox("blockchain-mailbox"); } private void SaveHeaderHashList(Snapshot snapshot = null) diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index c8dd59e3ae..9a51201c9c 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -8,6 +8,7 @@ using Neo.Wallets; using System; using System.Net; +using Neo.Network.P2P.Payloads; namespace Neo { @@ -26,14 +27,16 @@ public class NeoSystem : IDisposable public IActorRef Consensus { get; private set; } public RpcServer RpcServer { get; private set; } + private readonly Store blockchainStore; private readonly Store consensusStore; private Peer.Start start_message = null; private bool suspend = false; public NeoSystem(Store store, Store consensusStore = null) { + this.blockchainStore = store; this.consensusStore = consensusStore ?? store; - this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store, this.consensusStore)); + this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); Plugin.LoadPlugins(this); @@ -74,6 +77,11 @@ public void StartConsensus(Wallet wallet) }; if (!suspend) { + var consensusTransactions = Neo.Consensus.Helper.RetreiveTransactionsFromSavedConsensusContext( + Ledger.Blockchain.Singleton.MemPool, blockchainStore, consensusStore); + foreach (var tx in consensusTransactions) + Blockchain.Tell(tx, ActorRefs.NoSender); + LocalNode.Tell(start_message); start_message = null; } From 3dd2928fea7c841a006ab30242a232876625d46e Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 19 Feb 2019 12:05:18 -0800 Subject: [PATCH 26/34] Remove unused using statement. --- neo.UnitTests/UT_Consensus.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/neo.UnitTests/UT_Consensus.cs b/neo.UnitTests/UT_Consensus.cs index f03ba98bca..75f4f6c27e 100644 --- a/neo.UnitTests/UT_Consensus.cs +++ b/neo.UnitTests/UT_Consensus.cs @@ -15,7 +15,6 @@ using System.Linq; using System.Numerics; using System.Security.Cryptography; -using Neo.Persistence.LevelDB; using ECPoint = Neo.Cryptography.ECC.ECPoint; namespace Neo.UnitTests From 2fc2139470ea2ce98dc7f31ad13b99ba51bed993 Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 19 Feb 2019 12:20:21 -0800 Subject: [PATCH 27/34] Clean-up. --- neo/Consensus/Helper.cs | 4 ++-- neo/NeoSystem.cs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index 6b8539a187..aa41443956 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -40,11 +40,11 @@ internal static bool LoadContextFromStore(this IConsensusContext context, Store return false; } - internal static IEnumerable RetreiveTransactionsFromSavedConsensusContext(MemoryPool memoryPool, Store store, Store consensusStore) + internal static IEnumerable RetreiveTransactionsFromSavedConsensusContext(Store consensusStore) { IConsensusContext context = new ConsensusContext(null); context.LoadContextFromStore(consensusStore, false); - return context.Transactions?.Values; + return context.Transactions?.Values ?? (IEnumerable) new Transaction[0]; } } } diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index 9a51201c9c..7b02d0a4e0 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -77,8 +77,7 @@ public void StartConsensus(Wallet wallet) }; if (!suspend) { - var consensusTransactions = Neo.Consensus.Helper.RetreiveTransactionsFromSavedConsensusContext( - Ledger.Blockchain.Singleton.MemPool, blockchainStore, consensusStore); + var consensusTransactions = Neo.Consensus.Helper.RetreiveTransactionsFromSavedConsensusContext(consensusStore); foreach (var tx in consensusTransactions) Blockchain.Tell(tx, ActorRefs.NoSender); From fc4cfe8c8173e92ba22796b00eb779e405b200c1 Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 19 Feb 2019 12:24:52 -0800 Subject: [PATCH 28/34] More clean-up. Remove unused using statements. --- neo/Consensus/Helper.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index aa41443956..cd5d5e9c48 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -1,12 +1,9 @@ using System.Collections.Generic; -using System.Collections.ObjectModel; using System.IO; using Neo.IO; using Neo.IO.Data.LevelDB; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Persistence.LevelDB; namespace Neo.Consensus { From 14a0a2cf16559e018a4b8f8bcfe3c62262dcf84c Mon Sep 17 00:00:00 2001 From: jsolman Date: Tue, 19 Feb 2019 12:28:19 -0800 Subject: [PATCH 29/34] Remove one more unused using statement. --- neo/NeoSystem.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index 7b02d0a4e0..66fde45d3d 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -8,7 +8,6 @@ using Neo.Wallets; using System; using System.Net; -using Neo.Network.P2P.Payloads; namespace Neo { From 3d6a2b12e3178a05d18e2afa95890eb48f2f0492 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 Feb 2019 14:54:41 +0800 Subject: [PATCH 30/34] Minor changes --- neo/Consensus/ConsensusService.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 0340ec0c37..9527e8ad00 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -326,7 +326,6 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m context.PreparationPayloads[i] = null; context.PreparationPayloads[payload.ValidatorIndex] = payload; byte[] hashData = context.MakeHeader().GetHashData(); - for (int i = 0; i < context.CommitPayloads.Length; i++) if (context.CommitPayloads[i] != null) if (!Crypto.Default.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage().Signature, context.Validators[i].EncodePoint(false))) @@ -410,7 +409,7 @@ private void OnStart(Start options) { Log("OnStart"); started = true; - bool loadedState = options.IgnoreRecoveryLogs ? false : context.LoadContextFromStore(store); + bool loadedState = !options.IgnoreRecoveryLogs && context.LoadContextFromStore(store); if (loadedState && context.State.HasFlag(ConsensusState.CommitSent) && Blockchain.Singleton.Height + 1 == context.BlockIndex) { CheckPreparations(); From 438c105c85f4347a8a068cdda0b789bcb88883fa Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 Feb 2019 15:04:43 +0800 Subject: [PATCH 31/34] Add method `PutSync()` to class `Store` --- neo/Consensus/Helper.cs | 21 +++++++++------------ neo/Persistence/LevelDB/LevelDBStore.cs | 11 +++++++---- neo/Persistence/Store.cs | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index cd5d5e9c48..baddd395ac 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -1,27 +1,24 @@ -using System.Collections.Generic; -using System.IO; -using Neo.IO; -using Neo.IO.Data.LevelDB; +using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; +using System.Collections.Generic; +using System.IO; namespace Neo.Consensus { - public static class Helper + internal static class Helper { /// /// Prefix for saving consensus state. /// public const byte CN_Context = 0xf4; - private static readonly WriteOptions SynchronousWriteOptions = new WriteOptions { Sync = true }; - - internal static void WriteContextToStore(this IConsensusContext context, Store store) + public static void WriteContextToStore(this IConsensusContext context, Store store) { - store.Put(CN_Context, new byte[0], context.ToArray(), SynchronousWriteOptions); + store.PutSync(CN_Context, new byte[0], context.ToArray()); } - internal static bool LoadContextFromStore(this IConsensusContext context, Store store, bool shouldReset=true) + public static bool LoadContextFromStore(this IConsensusContext context, Store store, bool shouldReset = true) { byte[] data = store.Get(CN_Context, new byte[0]); if (data != null) @@ -37,11 +34,11 @@ internal static bool LoadContextFromStore(this IConsensusContext context, Store return false; } - internal static IEnumerable RetreiveTransactionsFromSavedConsensusContext(Store consensusStore) + public static IEnumerable RetreiveTransactionsFromSavedConsensusContext(Store consensusStore) { IConsensusContext context = new ConsensusContext(null); context.LoadContextFromStore(consensusStore, false); - return context.Transactions?.Values ?? (IEnumerable) new Transaction[0]; + return context.Transactions?.Values ?? (IEnumerable)new Transaction[0]; } } } diff --git a/neo/Persistence/LevelDB/LevelDBStore.cs b/neo/Persistence/LevelDB/LevelDBStore.cs index 5374819d21..b74034f1ca 100644 --- a/neo/Persistence/LevelDB/LevelDBStore.cs +++ b/neo/Persistence/LevelDB/LevelDBStore.cs @@ -112,11 +112,14 @@ public override MetaDataCache GetHeaderHashIndex() return new DbMetaDataCache(db, null, null, Prefixes.IX_CurrentHeader); } - public override void Put(byte prefix, byte[] key, byte[] value, WriteOptions writeOptions = null) + public override void Put(byte prefix, byte[] key, byte[] value) { - if (writeOptions == null) - writeOptions = WriteOptions.Default; - db.Put(writeOptions, SliceBuilder.Begin(prefix).Add(key), value); + db.Put(WriteOptions.Default, SliceBuilder.Begin(prefix).Add(key), value); + } + + public override void PutSync(byte prefix, byte[] key, byte[] value) + { + db.Put(new WriteOptions { Sync = true }, SliceBuilder.Begin(prefix).Add(key), value); } } } diff --git a/neo/Persistence/Store.cs b/neo/Persistence/Store.cs index e9e8035f2e..fd7e8b381b 100644 --- a/neo/Persistence/Store.cs +++ b/neo/Persistence/Store.cs @@ -1,6 +1,5 @@ using Neo.Cryptography.ECC; using Neo.IO.Caching; -using Neo.IO.Data.LevelDB; using Neo.IO.Wrappers; using Neo.Ledger; @@ -36,7 +35,8 @@ public abstract class Store : IPersistence public abstract MetaDataCache GetValidatorsCount(); public abstract MetaDataCache GetBlockHashIndex(); public abstract MetaDataCache GetHeaderHashIndex(); - public abstract void Put(byte prefix, byte[] key, byte[] value, WriteOptions writeOptions = null); + public abstract void Put(byte prefix, byte[] key, byte[] value); + public abstract void PutSync(byte prefix, byte[] key, byte[] value); public abstract Snapshot GetSnapshot(); } From 3ebb75e0ded700ee46e60d762922ab7c58355bee Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 21 Feb 2019 13:27:38 +0800 Subject: [PATCH 32/34] Renaming --- neo/Consensus/ConsensusService.cs | 4 ++-- neo/Consensus/Helper.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 70300555b1..c187680e97 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -125,7 +125,7 @@ private void CheckPreparations() ConsensusPayload payload = context.MakeCommit(); Log($"send commit"); context.State |= ConsensusState.CommitSent; - context.WriteContextToStore(store); + context.Save(store); localNode.Tell(new LocalNode.SendDirectly { Inventory = payload }); // Set timer, so we will resend the commit in case of a networking issue ChangeTimer(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock)); @@ -430,7 +430,7 @@ private void OnStart(Start options) { Log("OnStart"); started = true; - bool loadedState = !options.IgnoreRecoveryLogs && context.LoadContextFromStore(store); + bool loadedState = !options.IgnoreRecoveryLogs && context.Load(store); if (loadedState && context.State.HasFlag(ConsensusState.CommitSent) && Blockchain.Singleton.Height + 1 == context.BlockIndex) { CheckPreparations(); diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index baddd395ac..ac2d656bb5 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -13,12 +13,12 @@ internal static class Helper /// public const byte CN_Context = 0xf4; - public static void WriteContextToStore(this IConsensusContext context, Store store) + public static void Save(this IConsensusContext context, Store store) { store.PutSync(CN_Context, new byte[0], context.ToArray()); } - public static bool LoadContextFromStore(this IConsensusContext context, Store store, bool shouldReset = true) + public static bool Load(this IConsensusContext context, Store store, bool shouldReset = true) { byte[] data = store.Get(CN_Context, new byte[0]); if (data != null) @@ -37,7 +37,7 @@ public static bool LoadContextFromStore(this IConsensusContext context, Store st public static IEnumerable RetreiveTransactionsFromSavedConsensusContext(Store consensusStore) { IConsensusContext context = new ConsensusContext(null); - context.LoadContextFromStore(consensusStore, false); + context.Load(consensusStore, false); return context.Transactions?.Values ?? (IEnumerable)new Transaction[0]; } } From 75c81d9c23664616175d81441964550ab7abb9ee Mon Sep 17 00:00:00 2001 From: jsolman Date: Wed, 20 Feb 2019 21:55:48 -0800 Subject: [PATCH 33/34] Remove unused variable. --- neo/NeoSystem.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index c470f2c005..e34e5b06ec 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -27,14 +27,12 @@ public class NeoSystem : IDisposable public IActorRef Consensus { get; private set; } public RpcServer RpcServer { get; private set; } - private readonly Store blockchainStore; private readonly Store consensusStore; private Peer.Start start_message = null; private bool suspend = false; public NeoSystem(Store store, Store consensusStore = null) { - this.blockchainStore = store; this.consensusStore = consensusStore ?? store; this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); From becf0f59c6ec858a8caa250f442b965c39855e98 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 21 Feb 2019 14:16:39 +0800 Subject: [PATCH 34/34] Optimize the serialization of `ConsensusContext` --- neo.UnitTests/UT_Consensus.cs | 20 ++++++++++---------- neo/Consensus/ConsensusContext.cs | 21 ++++++++------------- neo/Consensus/ConsensusService.cs | 2 +- neo/Consensus/Helper.cs | 19 +++++++++++-------- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/neo.UnitTests/UT_Consensus.cs b/neo.UnitTests/UT_Consensus.cs index 75f4f6c27e..b31a5508f8 100644 --- a/neo.UnitTests/UT_Consensus.cs +++ b/neo.UnitTests/UT_Consensus.cs @@ -178,20 +178,20 @@ public void TestSerializeAndDeserializeConsensusContext() { var consensusContext = new ConsensusContext(null); consensusContext.State = ConsensusState.CommitSent; - consensusContext.PrevHash = UInt256.Parse("3333333377777777333333337777777733333333777777773333333377777777"); - consensusContext.BlockIndex = 1337; + consensusContext.PrevHash = UInt256.Parse("0xd42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf"); + consensusContext.BlockIndex = 1; consensusContext.ViewNumber = 2; consensusContext.Validators = new ECPoint[7] { - TestUtils.StandbyValidators[0], - ECPoint.Multiply(TestUtils.StandbyValidators[0], new BigInteger(2)), - ECPoint.Multiply(TestUtils.StandbyValidators[0], new BigInteger(3)), - ECPoint.Multiply(TestUtils.StandbyValidators[0], new BigInteger(4)), - ECPoint.Multiply(TestUtils.StandbyValidators[0], new BigInteger(5)), - ECPoint.Multiply(TestUtils.StandbyValidators[0], new BigInteger(6)), - ECPoint.Multiply(TestUtils.StandbyValidators[0], new BigInteger(7)), + ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", Cryptography.ECC.ECCurve.Secp256r1) }; - consensusContext.MyIndex = 3; + consensusContext.MyIndex = -1; consensusContext.PrimaryIndex = 6; consensusContext.Timestamp = 4244941711; consensusContext.Nonce = UInt64.MaxValue; diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index dd1257f79a..626a5b2931 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -17,9 +17,8 @@ namespace Neo.Consensus internal class ConsensusContext : IConsensusContext { public const uint Version = 0; - public ConsensusState State { get; set; } - public UInt256 PrevHash { get; set; } public uint BlockIndex { get; set; } + public UInt256 PrevHash { get; set; } public byte ViewNumber { get; set; } public ECPoint[] Validators { get; set; } public int MyIndex { get; set; } @@ -33,6 +32,7 @@ internal class ConsensusContext : IConsensusContext public ConsensusPayload[] CommitPayloads { get; set; } public ConsensusPayload[] ChangeViewPayloads { get; set; } public ConsensusPayload[] LastChangeViewPayloads { get; set; } + public ConsensusState State { get; set; } public Snapshot Snapshot { get; private set; } private KeyPair keyPair; private readonly Wallet wallet; @@ -66,13 +66,10 @@ public Block CreateBlock() public void Deserialize(BinaryReader reader) { - if (reader.ReadUInt32() != Version) return; - State = (ConsensusState)reader.ReadByte(); - PrevHash = reader.ReadSerializable(); - BlockIndex = reader.ReadUInt32(); + Reset(0); + if (reader.ReadUInt32() != Version) throw new FormatException(); + if (reader.ReadUInt32() != BlockIndex) throw new InvalidOperationException(); ViewNumber = reader.ReadByte(); - Validators = reader.ReadSerializableArray(); - MyIndex = reader.ReadInt32(); PrimaryIndex = reader.ReadUInt32(); Timestamp = reader.ReadUInt32(); Nonce = reader.ReadUInt64(); @@ -105,6 +102,7 @@ public void Deserialize(BinaryReader reader) LastChangeViewPayloads = new ConsensusPayload[reader.ReadVarInt()]; for (int i = 0; i < LastChangeViewPayloads.Length; i++) LastChangeViewPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; + State = (ConsensusState)reader.ReadByte(); } public void Dispose() @@ -251,7 +249,7 @@ public void Reset(byte viewNumber) keyPair = null; for (int i = 0; i < Validators.Length; i++) { - WalletAccount account = wallet.GetAccount(Validators[i]); + WalletAccount account = wallet?.GetAccount(Validators[i]); if (account?.HasKey != true) continue; MyIndex = i; keyPair = account.GetKey(); @@ -279,12 +277,8 @@ public void Reset(byte viewNumber) public void Serialize(BinaryWriter writer) { writer.Write(Version); - writer.Write((byte)State); - writer.Write(PrevHash); writer.Write(BlockIndex); writer.Write(ViewNumber); - writer.Write(Validators); - writer.Write(MyIndex); writer.Write(PrimaryIndex); writer.Write(Timestamp); writer.Write(Nonce); @@ -323,6 +317,7 @@ public void Serialize(BinaryWriter writer) if (!hasPayload) continue; writer.Write(payload); } + writer.Write((byte)State); } public void Fill() diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index c187680e97..50c0dbfcd6 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -431,7 +431,7 @@ private void OnStart(Start options) Log("OnStart"); started = true; bool loadedState = !options.IgnoreRecoveryLogs && context.Load(store); - if (loadedState && context.State.HasFlag(ConsensusState.CommitSent) && Blockchain.Singleton.Height + 1 == context.BlockIndex) + if (loadedState && context.State.HasFlag(ConsensusState.CommitSent)) { CheckPreparations(); return; diff --git a/neo/Consensus/Helper.cs b/neo/Consensus/Helper.cs index ac2d656bb5..16cf0f0778 100644 --- a/neo/Consensus/Helper.cs +++ b/neo/Consensus/Helper.cs @@ -18,26 +18,29 @@ public static void Save(this IConsensusContext context, Store store) store.PutSync(CN_Context, new byte[0], context.ToArray()); } - public static bool Load(this IConsensusContext context, Store store, bool shouldReset = true) + public static bool Load(this IConsensusContext context, Store store) { byte[] data = store.Get(CN_Context, new byte[0]); - if (data != null) + if (data is null) return false; + using (MemoryStream ms = new MemoryStream(data, false)) + using (BinaryReader reader = new BinaryReader(ms)) { - if (shouldReset) context.Reset(0); - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader reader = new BinaryReader(ms)) + try { context.Deserialize(reader); - return true; } + catch + { + return false; + } + return true; } - return false; } public static IEnumerable RetreiveTransactionsFromSavedConsensusContext(Store consensusStore) { IConsensusContext context = new ConsensusContext(null); - context.Load(consensusStore, false); + context.Load(consensusStore); return context.Transactions?.Values ?? (IEnumerable)new Transaction[0]; } }