From 78a3e57a23dde695f14ecc30f33794716bdd6a4e Mon Sep 17 00:00:00 2001 From: ZhangTao Date: Mon, 29 Jun 2020 16:16:36 +0800 Subject: [PATCH 01/19] [neox] persistence (#1721) * don't use snapshot directly in plugin * persistence and ut * fix mpt * precommit * fix commit * rm LocalRootHashIndex * fix ut * pre commit * fix clone view * change name * rename * abstract * comment * fix ReadOnlyView * rm precommit * rm HashState * add MPTDataCache * optimze * optimize * remove blank line * StateRoot verify fee * expose Root in MPTDataCache * fix some and ut * merge master * master * fix mpt ut * fix Storages and name * rename * add comment * proof prefix * fix * format * format * format * add StateRoot ut * reset mpt prefix * rm GetMessage * throw exception when no script hash in state root * UpdateLocalStateRoot when Storages changed * rename Co-authored-by: Tommo-L --- src/neo/Cryptography/MPT/MPTTrie.cs | 2 +- src/neo/Ledger/Blockchain.cs | 1 + src/neo/Network/P2P/MessageCommand.cs | 2 + src/neo/Network/P2P/Payloads/InventoryType.cs | 1 + src/neo/Network/P2P/Payloads/StateRoot.cs | 122 ++++++++++++++++++ src/neo/Persistence/ClonedView.cs | 6 + src/neo/Persistence/Helper.cs | 9 ++ src/neo/Persistence/MPTDataCache.cs | 53 ++++++++ src/neo/Persistence/Prefixes.cs | 3 +- src/neo/Persistence/ReadOnlyView.cs | 6 +- src/neo/Persistence/SnapshotView.cs | 7 +- src/neo/Persistence/StoreView.cs | 5 + tests/neo.UnitTests/Consensus/UT_Consensus.cs | 3 + .../Cryptography/MPT/UT_MPTTrie.cs | 3 +- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 1 + .../Network/P2P/Payloads/UT_StateRoot.cs | 76 +++++++++++ .../Network/P2P/Payloads/UT_Transaction.cs | 14 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 10 +- 18 files changed, 312 insertions(+), 12 deletions(-) create mode 100644 src/neo/Network/P2P/Payloads/StateRoot.cs create mode 100644 src/neo/Persistence/MPTDataCache.cs create mode 100644 tests/neo.UnitTests/Network/P2P/Payloads/UT_StateRoot.cs diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index df97b54f9c..006e4280f3 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -18,7 +18,7 @@ public partial class MPTTrie public MPTTrie(ISnapshot store, UInt256 root) { this.store = store ?? throw new ArgumentNullException(); - this.root = root is null ? HashNode.EmptyNode : new HashNode(root); + this.root = root is null || root == UInt256.Zero ? HashNode.EmptyNode : new HashNode(root); } private MPTNode Resolve(HashNode n) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 2c9981f348..4e545d72e8 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -445,6 +445,7 @@ private void Persist(Block block) snapshot.BlockHashIndex.GetAndChange().Set(block); foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) plugin.OnPersist(snapshot, all_application_executed); + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); List commitExceptions = null; foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index 89c21597cf..1c3ce68659 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -40,6 +40,8 @@ public enum MessageCommand : byte Block = 0x2c, [ReflectionCache(typeof(ConsensusPayload))] Consensus = 0x2d, + [ReflectionCache(typeof(StateRoot))] + StateRoot = 0x2e, Reject = 0x2f, //SPV protocol diff --git a/src/neo/Network/P2P/Payloads/InventoryType.cs b/src/neo/Network/P2P/Payloads/InventoryType.cs index 0a1b831d12..9effb99556 100644 --- a/src/neo/Network/P2P/Payloads/InventoryType.cs +++ b/src/neo/Network/P2P/Payloads/InventoryType.cs @@ -4,6 +4,7 @@ public enum InventoryType : byte { TX = MessageCommand.Transaction, Block = MessageCommand.Block, + StateRoot = MessageCommand.StateRoot, Consensus = MessageCommand.Consensus } } diff --git a/src/neo/Network/P2P/Payloads/StateRoot.cs b/src/neo/Network/P2P/Payloads/StateRoot.cs new file mode 100644 index 0000000000..e6da3b7e9e --- /dev/null +++ b/src/neo/Network/P2P/Payloads/StateRoot.cs @@ -0,0 +1,122 @@ +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using System; +using System.IO; + +namespace Neo.Network.P2P.Payloads +{ + public class StateRoot : ICloneable, IInventory + { + public byte Version; + public uint Index; + public UInt256 RootHash; + public Witness Witness; + + InventoryType IInventory.InventoryType => InventoryType.StateRoot; + + private UInt256 _hash = null; + + public UInt256 Hash + { + get + { + if (_hash == null) + { + _hash = new UInt256(Crypto.Hash256(this.GetHashData())); + } + return _hash; + } + } + + Witness[] IVerifiable.Witnesses + { + get + { + return new[] { Witness }; + } + set + { + if (value.Length != 1) throw new ArgumentException(); + Witness = value[0]; + } + } + + public int Size => + sizeof(byte) + //Version + sizeof(uint) + //Index + UInt256.Length + //Root + Witness.Size; //Witness + + StateRoot ICloneable.Clone() + { + return new StateRoot + { + Version = Version, + Index = Index, + RootHash = RootHash, + Witness = Witness, + }; + } + + void ICloneable.FromReplica(StateRoot replica) + { + Version = replica.Version; + Index = replica.Index; + RootHash = replica.RootHash; + Witness = replica.Witness; + } + + public void Deserialize(BinaryReader reader) + { + this.DeserializeUnsigned(reader); + Witness = reader.ReadSerializable(); + } + + public void DeserializeUnsigned(BinaryReader reader) + { + Version = reader.ReadByte(); + Index = reader.ReadUInt32(); + RootHash = reader.ReadSerializable(); + } + + public void Serialize(BinaryWriter writer) + { + this.SerializeUnsigned(writer); + writer.Write(Witness); + } + + public void SerializeUnsigned(BinaryWriter writer) + { + writer.Write(Version); + writer.Write(Index); + writer.Write(RootHash); + } + + public bool Verify(StoreView snapshot) + { + return this.VerifyWitnesses(snapshot, 1_00000000); + } + + public virtual UInt160[] GetScriptHashesForVerifying(StoreView snapshot) + { + var script_hash = Blockchain.Singleton.GetBlock(Index)?.NextConsensus; + if (script_hash is null) throw new System.InvalidOperationException("No script hash for state root verifying"); + return new UInt160[] { script_hash }; + } + + public JObject ToJson() + { + var json = new JObject(); + json["version"] = Version; + json["index"] = Index; + json["stateroot"] = RootHash.ToString(); + json["witness"] = Witness.ToJson(); + return json; + } + } +} diff --git a/src/neo/Persistence/ClonedView.cs b/src/neo/Persistence/ClonedView.cs index 307438b2db..025b9c03f5 100644 --- a/src/neo/Persistence/ClonedView.cs +++ b/src/neo/Persistence/ClonedView.cs @@ -1,6 +1,8 @@ +using Neo.Cryptography.MPT; using Neo.IO; using Neo.IO.Caching; using Neo.Ledger; +using Neo.Network.P2P.Payloads; namespace Neo.Persistence { @@ -11,8 +13,10 @@ internal class ClonedView : StoreView public override DataCache Contracts { get; } public override DataCache Storages { get; } public override DataCache, HeaderHashList> HeaderHashList { get; } + public override DataCache, HashIndexState> LocalStateRoot { get; } public override MetaDataCache BlockHashIndex { get; } public override MetaDataCache HeaderHashIndex { get; } + public override MetaDataCache ValidatorsStateRoot { get; } public override MetaDataCache ContractId { get; } public ClonedView(StoreView view) @@ -23,8 +27,10 @@ public ClonedView(StoreView view) this.Contracts = view.Contracts.CreateSnapshot(); this.Storages = view.Storages.CreateSnapshot(); this.HeaderHashList = view.HeaderHashList.CreateSnapshot(); + this.LocalStateRoot = view.LocalStateRoot.CreateSnapshot(); this.BlockHashIndex = view.BlockHashIndex.CreateSnapshot(); this.HeaderHashIndex = view.HeaderHashIndex.CreateSnapshot(); + this.ValidatorsStateRoot = view.ValidatorsStateRoot.CreateSnapshot(); this.ContractId = view.ContractId.CreateSnapshot(); } } diff --git a/src/neo/Persistence/Helper.cs b/src/neo/Persistence/Helper.cs index 51d9314bfc..965d4566a6 100644 --- a/src/neo/Persistence/Helper.cs +++ b/src/neo/Persistence/Helper.cs @@ -1,4 +1,5 @@ using System; +using Neo.Ledger; namespace Neo.Persistence { @@ -8,5 +9,13 @@ public static byte[] EnsureNotNull(this byte[] source) { return source ?? Array.Empty(); } + + public static void UpdateLocalStateRoot(this SnapshotView snapshot) + { + snapshot.Storages.Commit(); + var root = snapshot.LocalStateRoot.GetAndChange(snapshot.Height, () => new HashIndexState()); + root.Index = snapshot.Height; + root.Hash = ((MPTDataCache)snapshot.Storages).Root.Hash; + } } } diff --git a/src/neo/Persistence/MPTDataCache.cs b/src/neo/Persistence/MPTDataCache.cs new file mode 100644 index 0000000000..7463d58859 --- /dev/null +++ b/src/neo/Persistence/MPTDataCache.cs @@ -0,0 +1,53 @@ + +using Neo.Cryptography.MPT; +using Neo.IO; +using Neo.IO.Caching; +using System; +using System.Collections.Generic; + +namespace Neo.Persistence +{ + internal class MPTDataCache : DataCache + where TKey : IEquatable, ISerializable, new() + where TValue : class, ICloneable, ISerializable, new() + { + private MPTTrie mptTrie; + + public MPTNode Root => mptTrie.Root; + + public MPTDataCache(IReadOnlyStore store, UInt256 root) + { + mptTrie = new MPTTrie(store as ISnapshot, root); + } + + protected override void AddInternal(TKey key, TValue value) + { + mptTrie.Put(key, value); + } + + protected override void DeleteInternal(TKey key) + { + mptTrie.Delete(key); + } + + protected override IEnumerable<(TKey Key, TValue Value)> FindInternal(byte[] key_prefix) + { + return mptTrie.Find(key_prefix); + } + + protected override TValue GetInternal(TKey key) + { + return mptTrie[key]; + } + + protected override TValue TryGetInternal(TKey key) + { + return mptTrie[key]; + } + + protected override void UpdateInternal(TKey key, TValue value) + { + mptTrie.Put(key, value); + } + } +} diff --git a/src/neo/Persistence/Prefixes.cs b/src/neo/Persistence/Prefixes.cs index 421fe086c3..744f027d2d 100644 --- a/src/neo/Persistence/Prefixes.cs +++ b/src/neo/Persistence/Prefixes.cs @@ -6,12 +6,13 @@ internal static class Prefixes public const byte DATA_Transaction = 0x02; public const byte ST_Contract = 0x50; - public const byte ST_Storage = 0x70; + public const byte ST_LocalStateRoot = 0x60; public const byte IX_HeaderHashList = 0x80; public const byte IX_CurrentBlock = 0xc0; public const byte IX_CurrentHeader = 0xc1; public const byte IX_ContractId = 0xc2; + public const byte IX_ValidatorsStateRoot = 0xc4; /* Prefixes 0xf0 to 0xff are reserved for external use. * diff --git a/src/neo/Persistence/ReadOnlyView.cs b/src/neo/Persistence/ReadOnlyView.cs index 5f87f236ab..b2147f2c34 100644 --- a/src/neo/Persistence/ReadOnlyView.cs +++ b/src/neo/Persistence/ReadOnlyView.cs @@ -1,6 +1,8 @@ +using Neo.Cryptography.MPT; using Neo.IO; using Neo.IO.Caching; using Neo.Ledger; +using Neo.Network.P2P.Payloads; using System; namespace Neo.Persistence @@ -15,10 +17,12 @@ public class ReadOnlyView : StoreView public override DataCache Blocks => new StoreDataCache(store, Prefixes.DATA_Block); public override DataCache Transactions => new StoreDataCache(store, Prefixes.DATA_Transaction); public override DataCache Contracts => new StoreDataCache(store, Prefixes.ST_Contract); - public override DataCache Storages => new StoreDataCache(store, Prefixes.ST_Storage); + public override DataCache Storages => new MPTDataCache(store, CurrentStateRootHash); public override DataCache, HeaderHashList> HeaderHashList => new StoreDataCache, HeaderHashList>(store, Prefixes.IX_HeaderHashList); + public override DataCache, HashIndexState> LocalStateRoot => new StoreDataCache, HashIndexState>(store, Prefixes.ST_LocalStateRoot); public override MetaDataCache BlockHashIndex => new StoreMetaDataCache(store, Prefixes.IX_CurrentBlock); public override MetaDataCache HeaderHashIndex => new StoreMetaDataCache(store, Prefixes.IX_CurrentHeader); + public override MetaDataCache ValidatorsStateRoot => new StoreMetaDataCache(store, Prefixes.IX_ValidatorsStateRoot); public override MetaDataCache ContractId => new StoreMetaDataCache(store, Prefixes.IX_ContractId); public ReadOnlyView(IReadOnlyStore store) diff --git a/src/neo/Persistence/SnapshotView.cs b/src/neo/Persistence/SnapshotView.cs index d634200134..2bbffc0cb5 100644 --- a/src/neo/Persistence/SnapshotView.cs +++ b/src/neo/Persistence/SnapshotView.cs @@ -1,6 +1,7 @@ using Neo.IO; using Neo.IO.Caching; using Neo.Ledger; +using Neo.Network.P2P.Payloads; using System; namespace Neo.Persistence @@ -17,8 +18,10 @@ public class SnapshotView : StoreView, IDisposable public override DataCache Contracts { get; } public override DataCache Storages { get; } public override DataCache, HeaderHashList> HeaderHashList { get; } + public override DataCache, HashIndexState> LocalStateRoot { get; } public override MetaDataCache BlockHashIndex { get; } public override MetaDataCache HeaderHashIndex { get; } + public override MetaDataCache ValidatorsStateRoot { get; } public override MetaDataCache ContractId { get; } public SnapshotView(IStore store) @@ -27,11 +30,13 @@ public SnapshotView(IStore store) Blocks = new StoreDataCache(snapshot, Prefixes.DATA_Block); Transactions = new StoreDataCache(snapshot, Prefixes.DATA_Transaction); Contracts = new StoreDataCache(snapshot, Prefixes.ST_Contract); - Storages = new StoreDataCache(snapshot, Prefixes.ST_Storage); HeaderHashList = new StoreDataCache, HeaderHashList>(snapshot, Prefixes.IX_HeaderHashList); + LocalStateRoot = new StoreDataCache, HashIndexState>(snapshot, Prefixes.ST_LocalStateRoot); BlockHashIndex = new StoreMetaDataCache(snapshot, Prefixes.IX_CurrentBlock); HeaderHashIndex = new StoreMetaDataCache(snapshot, Prefixes.IX_CurrentHeader); ContractId = new StoreMetaDataCache(snapshot, Prefixes.IX_ContractId); + ValidatorsStateRoot = new StoreMetaDataCache(snapshot, Prefixes.IX_ValidatorsStateRoot); + Storages = new MPTDataCache(snapshot, CurrentStateRootHash);//Need BlockHashIndex and LocalStateRoot loaded. } public override void Commit() diff --git a/src/neo/Persistence/StoreView.cs b/src/neo/Persistence/StoreView.cs index c0126b0c95..7fd39901fe 100644 --- a/src/neo/Persistence/StoreView.cs +++ b/src/neo/Persistence/StoreView.cs @@ -16,13 +16,16 @@ public abstract class StoreView public abstract DataCache Contracts { get; } public abstract DataCache Storages { get; } public abstract DataCache, HeaderHashList> HeaderHashList { get; } + public abstract DataCache, HashIndexState> LocalStateRoot { get; } public abstract MetaDataCache BlockHashIndex { get; } public abstract MetaDataCache HeaderHashIndex { get; } + public abstract MetaDataCache ValidatorsStateRoot { get; } public abstract MetaDataCache ContractId { get; } public uint Height => BlockHashIndex.Get().Index; public uint HeaderHeight => HeaderHashIndex.Get().Index; public UInt256 CurrentBlockHash => BlockHashIndex.Get().Hash; + public UInt256 CurrentStateRootHash => LocalStateRoot.TryGet(Height)?.Hash ?? UInt256.Zero; public UInt256 CurrentHeaderHash => HeaderHashIndex.Get().Hash; public StoreView Clone() @@ -37,9 +40,11 @@ public virtual void Commit() Contracts.Commit(); Storages.Commit(); HeaderHashList.Commit(); + LocalStateRoot.Commit(); BlockHashIndex.Commit(); HeaderHashIndex.Commit(); ContractId.Commit(); + ValidatorsStateRoot.Commit(); } public bool ContainsBlock(UInt256 hash) diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 5bcf9f031c..d756843a2f 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -10,6 +10,7 @@ using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests.Cryptography; @@ -289,6 +290,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm { Value = mockContext.Object.Validators.ToByteArray() }); + mockContext.Object.Snapshot.UpdateLocalStateRoot(); mockContext.Object.Snapshot.Commit(); // =============================================================== @@ -425,6 +427,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("mockContext Reset for returning Blockchain.Singleton snapshot to original state."); mockContext.Object.Reset(0); mockContext.Object.Snapshot.Storages.Delete(CreateStorageKeyForNativeNeo(14)); + mockContext.Object.Snapshot.UpdateLocalStateRoot(); mockContext.Object.Snapshot.Commit(); Console.WriteLine("mockContext Reset."); diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index 556d338ca8..2609ed7d6c 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -86,12 +86,13 @@ public static implicit operator TestValue(byte[] value) [TestClass] public class UT_MPTTrie { + private byte Prefix = 0xf0; private MPTNode root; private IStore mptdb; private void PutToStore(MPTNode node) { - mptdb.Put(0xf0, node.Hash.ToArray(), node.Encode()); + mptdb.Put(Prefix, node.Hash.ToArray(), node.Encode()); } [TestInitialize] diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index e4bf204d47..1e21081ee9 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -117,6 +117,7 @@ public void TestValidTransaction() var key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(acc.ScriptHash); var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); typeof(Blockchain) diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_StateRoot.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_StateRoot.cs new file mode 100644 index 0000000000..96b3b4f036 --- /dev/null +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_StateRoot.cs @@ -0,0 +1,76 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using System.IO; +using System.Text; + +namespace Neo.UnitTests.Network.P2P.Payloads +{ + [TestClass] + public class UT_StateRoot + { + StateRoot state_root; + + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + state_root = new StateRoot() + { + Version = 0, + Index = 1234, + RootHash = UInt256.Parse("5f4f1af77b127c9037c0204f682420a5ce621f3d8f4c8bdd9fd37422e0c58e9b"), + Witness = new Witness() + { + InvocationScript = new byte[] { 0x01 }, + VerificationScript = new byte[] { 0x02 } + } + }; + } + + [TestMethod] + public void TestSerializeUnsigned() + { + using MemoryStream ms = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(ms); + + state_root.SerializeUnsigned(writer); + + Assert.AreEqual("00d20400009b8ec5e02274d39fdd8b4c8f3d1f62cea52024684f20c037907c127bf71a4f5f", ms.ToArray().ToHexString()); + } + + [TestMethod] + public void TestDesializeUnsigned() + { + var data = "00d20400009b8ec5e02274d39fdd8b4c8f3d1f62cea52024684f20c037907c127bf71a4f5f".HexToBytes(); + using MemoryStream ms = new MemoryStream(data, false); + using BinaryReader reader = new BinaryReader(ms, Encoding.UTF8); + + var state_root_d = new StateRoot(); + state_root_d.DeserializeUnsigned(reader); + + Assert.AreEqual(state_root.Version, state_root_d.Version); + Assert.AreEqual(state_root.Index, state_root_d.Index); + Assert.AreEqual(state_root.RootHash, state_root_d.RootHash); + } + + [TestMethod] + public void TestGetScriptHashesForVerifying() + { + state_root.Index = 0; + var snapshot = Blockchain.Singleton.GetSnapshot(); + var scriptHashes = state_root.GetScriptHashesForVerifying(snapshot); + Assert.AreEqual(Blockchain.GenesisBlock.NextConsensus, scriptHashes[0]); + } + + [TestMethod] + public void TestToJson() + { + var json = state_root.ToJson(); + Assert.AreEqual(0, json["version"].AsNumber()); + Assert.AreEqual(1234, json["index"].AsNumber()); + Assert.AreEqual(state_root.RootHash.ToString(), json["stateroot"].AsString()); + } + } +} diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 9f80f21e22..5497a805ac 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -5,6 +5,7 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.SmartContract.Native.Tokens; @@ -130,6 +131,7 @@ public void FeeIsMultiSigContract() entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction @@ -199,7 +201,7 @@ public void FeeIsSignatureContractDetailed() var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction @@ -310,7 +312,7 @@ public void FeeIsSignatureContract_TestScope_Global() var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction @@ -396,7 +398,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction @@ -483,7 +485,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction @@ -623,7 +625,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction @@ -976,7 +978,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 82b1031fbc..459c5cadaa 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -3,6 +3,7 @@ using Neo.Cryptography.ECC; using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.SmartContract.Native.Tokens; @@ -205,12 +206,14 @@ public void TestGetAvailable() var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); wallet.GetAvailable(NativeContract.GAS.Hash).Should().Be(new BigDecimal(1000000000000, 8)); entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); } @@ -227,6 +230,7 @@ public void TestGetBalance() var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); wallet.GetBalance(UInt160.Zero, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(0, 0)); @@ -234,6 +238,7 @@ public void TestGetBalance() entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); } @@ -331,7 +336,7 @@ public void TestMakeTransaction1() key = NativeContract.NEO.CreateStorageKey(20, account.ScriptHash); var entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoToken.NeoAccountState())); entry2.GetInteroperable().Balance = 10000 * NativeContract.NEO.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); var tx = wallet.MakeTransaction(new TransferOutput[] @@ -360,6 +365,7 @@ public void TestMakeTransaction1() entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry1.GetInteroperable().Balance = 0; entry2.GetInteroperable().Balance = 0; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); } @@ -379,6 +385,7 @@ public void TestMakeTransaction2() var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 1000000 * NativeContract.GAS.Factor; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); var tx = wallet.MakeTransaction(new byte[] { }, account.ScriptHash, new[]{ new Signer() @@ -394,6 +401,7 @@ public void TestMakeTransaction2() entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); } From 4994632a9ffb585cfed3b97cbcc6a2ac38224f5b Mon Sep 17 00:00:00 2001 From: ZhangTao Date: Mon, 13 Jul 2020 18:49:50 +0800 Subject: [PATCH 02/19] [neox] consensus (#1719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * don't use snapshot directly in plugin * recover * consensus state root * fix build error * rename * remerge * rm GetMessage * rename * throw exception when no script hash in state root * Clean double enter * Clean big line * format * put root sig into request and response * update consensus ut * Check Json.Serialize map keys (#1705) * don't use snapshot directly in plugin * recover * consensus state root * fix build error * rename * remerge * rm GetMessage * rename * throw exception when no script hash in state root * Clean double enter * Clean big line * format * put root sig into request and response * update consensus ut * fix some * fix some * format * comment * fix * requst in recovery * StateRootSignature fixed size * no need ? * format * Update ConsensusContext.cs * Check sc.Complete Co-authored-by: Shargon --- src/neo/Consensus/ConsensusContext.cs | 50 +++++-- src/neo/Consensus/ConsensusService.cs | 16 +++ src/neo/Consensus/PrepareRequest.cs | 8 +- src/neo/Consensus/PrepareResponse.cs | 5 +- ...coveryMessage.PreparationPayloadCompact.cs | 17 ++- src/neo/Consensus/RecoveryMessage.cs | 3 +- src/neo/Ledger/Blockchain.state.cs | 12 ++ src/neo/Network/P2P/Payloads/StateRoot.cs | 8 +- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 128 +++++++++++++----- 9 files changed, 192 insertions(+), 55 deletions(-) create mode 100644 src/neo/Ledger/Blockchain.state.cs diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index b968f4dc47..8eaa9a56df 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -24,6 +24,7 @@ internal class ConsensusContext : IDisposable, ISerializable private const byte ConsensusStatePrefix = 0xf4; public Block Block; + public StateRoot PreviousBlockStateRoot; public byte ViewNumber; public ECPoint[] Validators; public int MyIndex; @@ -101,11 +102,31 @@ public Block CreateBlock() sc.AddSignature(contract, Validators[i], CommitPayloads[i].GetDeserializedMessage().Signature); j++; } + if (!sc.Completed) throw new Exception("There are not enough signatures for sign the Block."); Block.Witness = sc.GetWitnesses()[0]; Block.Transactions = TransactionHashes.Select(p => Transactions[p]).ToArray(); return Block; } + public StateRoot CreateStateRoot() + { + EnsureHeader(); + Contract contract = Contract.CreateMultiSigContract(M, Validators); + ContractParametersContext sc = new ContractParametersContext(PreviousBlockStateRoot); + for (int i = 0, j = 0; i < Validators.Length && j < M; i++) + { + if (PreparationPayloads[i]?.ConsensusMessage.ViewNumber != ViewNumber) continue; + if (i == GetPrimaryIndex(ViewNumber)) + sc.AddSignature(contract, Validators[i], PreparationPayloads[i].GetDeserializedMessage().StateRootSignature); + else + sc.AddSignature(contract, Validators[i], PreparationPayloads[i].GetDeserializedMessage().StateRootSignature); + j++; + } + if (!sc.Completed) throw new Exception("There are not enough signatures for sign the state root."); + PreviousBlockStateRoot.Witness = sc.GetWitnesses()[0]; + return PreviousBlockStateRoot; + } + public void Deserialize(BinaryReader reader) { Reset(0); @@ -147,6 +168,20 @@ public Block EnsureHeader() return Block; } + public StateRoot EnsureStateRoot() + { + if (PreviousBlockStateRoot is null) + { + PreviousBlockStateRoot = new StateRoot + { + Version = 0, + Index = Block.Index - 1, + RootHash = Blockchain.Singleton.GetLocalStateRoot(Block.Index - 1) + }; + } + return PreviousBlockStateRoot; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public uint GetPrimaryIndex(byte viewNumber) { @@ -314,7 +349,8 @@ public ConsensusPayload MakePrepareRequest() { Timestamp = Block.Timestamp, Nonce = Block.ConsensusData.Nonce, - TransactionHashes = TransactionHashes + TransactionHashes = TransactionHashes, + StateRootSignature = EnsureStateRoot().Sign(keyPair) }); } @@ -331,13 +367,7 @@ public ConsensusPayload MakeRecoveryMessage() PrepareRequest prepareRequestMessage = null; if (TransactionHashes != null) { - prepareRequestMessage = new PrepareRequest - { - ViewNumber = ViewNumber, - Timestamp = Block.Timestamp, - Nonce = Block.ConsensusData.Nonce, - TransactionHashes = TransactionHashes - }; + prepareRequestMessage = PreparationPayloads[GetPrimaryIndex(ViewNumber)].GetDeserializedMessage(); } return MakeSignedPayload(new RecoveryMessage() { @@ -356,7 +386,8 @@ public ConsensusPayload MakePrepareResponse() { return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareResponse { - PreparationHash = PreparationPayloads[Block.ConsensusData.PrimaryIndex].Hash + PreparationHash = PreparationPayloads[Block.ConsensusData.PrimaryIndex].Hash, + StateRootSignature = EnsureStateRoot().Sign(keyPair) }); } @@ -415,6 +446,7 @@ public void Reset(byte viewNumber) keyPair = account.GetKey(); break; } + PreviousBlockStateRoot = null; } else { diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index fbcf0437c1..7cb468b3af 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -169,6 +169,10 @@ private void CheckPreparations() 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)); + + StateRoot stateRoot = context.CreateStateRoot(); + Log($"relay state root, index={stateRoot.Index}, root_hash={stateRoot.RootHash}"); + blockchain.Tell(stateRoot); CheckCommits(); } } @@ -428,6 +432,12 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m Log($"Invalid request: transaction already exists", LogLevel.Warning); return; } + var stateRootHashData = context.EnsureStateRoot().GetHashData(); + if (!Crypto.VerifySignature(stateRootHashData, message.StateRootSignature, context.Validators[payload.ValidatorIndex])) + { + Log($"Invalid request: invalid state root signature", 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) @@ -490,6 +500,12 @@ private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse if (context.PreparationPayloads[payload.ValidatorIndex] != null || context.NotAcceptingPayloadsDueToViewChanging) return; if (context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex] != null && !message.PreparationHash.Equals(context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex].Hash)) return; + byte[] stateRootHashData = context.EnsureStateRoot().GetHashData(); + if (!Crypto.VerifySignature(stateRootHashData, message.StateRootSignature, context.Validators[payload.ValidatorIndex])) + { + Log($"Invalid response: invalid state root signature, height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}", LogLevel.Warning); + return; + } // Timeout extension: prepare response has been received with success // around 2*15/M=30.0/5 ~ 40% block time (for M=5) diff --git a/src/neo/Consensus/PrepareRequest.cs b/src/neo/Consensus/PrepareRequest.cs index 2369b4f9f7..659c5ff795 100644 --- a/src/neo/Consensus/PrepareRequest.cs +++ b/src/neo/Consensus/PrepareRequest.cs @@ -11,11 +11,13 @@ public class PrepareRequest : ConsensusMessage public ulong Timestamp; public ulong Nonce; public UInt256[] TransactionHashes; + public byte[] StateRootSignature; public override int Size => base.Size - + sizeof(ulong) //Timestamp + + sizeof(ulong) //Timestamp + sizeof(ulong) //Nonce - + TransactionHashes.GetVarSize(); //TransactionHashes + + TransactionHashes.GetVarSize() //TransactionHashes + + StateRootSignature.Length; //StateRootSignature public PrepareRequest() : base(ConsensusMessageType.PrepareRequest) @@ -30,6 +32,7 @@ public override void Deserialize(BinaryReader reader) TransactionHashes = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); if (TransactionHashes.Distinct().Count() != TransactionHashes.Length) throw new FormatException(); + StateRootSignature = reader.ReadFixedBytes(64); } public override void Serialize(BinaryWriter writer) @@ -38,6 +41,7 @@ public override void Serialize(BinaryWriter writer) writer.Write(Timestamp); writer.Write(Nonce); writer.Write(TransactionHashes); + writer.Write(StateRootSignature); } } } diff --git a/src/neo/Consensus/PrepareResponse.cs b/src/neo/Consensus/PrepareResponse.cs index 7c2956ccc2..b1628650f6 100644 --- a/src/neo/Consensus/PrepareResponse.cs +++ b/src/neo/Consensus/PrepareResponse.cs @@ -6,8 +6,9 @@ namespace Neo.Consensus public class PrepareResponse : ConsensusMessage { public UInt256 PreparationHash; + public byte[] StateRootSignature; - public override int Size => base.Size + PreparationHash.Size; + public override int Size => base.Size + PreparationHash.Size + StateRootSignature.Length; public PrepareResponse() : base(ConsensusMessageType.PrepareResponse) @@ -18,12 +19,14 @@ public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); PreparationHash = reader.ReadSerializable(); + StateRootSignature = reader.ReadFixedBytes(64); } public override void Serialize(BinaryWriter writer) { base.Serialize(writer); writer.Write(PreparationHash); + writer.Write(StateRootSignature); } } } diff --git a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs index 962ae69185..ceac4563fc 100644 --- a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs @@ -1,5 +1,6 @@ using Neo.IO; using Neo.Network.P2P.Payloads; +using System; using System.IO; namespace Neo.Consensus @@ -10,23 +11,32 @@ public class PreparationPayloadCompact : ISerializable { public ushort ValidatorIndex; public byte[] InvocationScript; + public byte[] StateRootSignature; int ISerializable.Size => - sizeof(ushort) + //ValidatorIndex - InvocationScript.GetVarSize(); //InvocationScript + sizeof(ushort) + //ValidatorIndex + InvocationScript.GetVarSize() + //InvocationScript + StateRootSignature.Length; //StateRootSignature void ISerializable.Deserialize(BinaryReader reader) { ValidatorIndex = reader.ReadUInt16(); InvocationScript = reader.ReadVarBytes(1024); + StateRootSignature = reader.ReadFixedBytes(64); } public static PreparationPayloadCompact FromPayload(ConsensusPayload payload) { + byte[] state_root_sig = Array.Empty(); + if (payload.ConsensusMessage is PrepareResponse req) + state_root_sig = req.StateRootSignature; + else if (payload.ConsensusMessage is PrepareResponse resp) + state_root_sig = resp.StateRootSignature; return new PreparationPayloadCompact { ValidatorIndex = payload.ValidatorIndex, - InvocationScript = payload.Witness.InvocationScript + InvocationScript = payload.Witness.InvocationScript, + StateRootSignature = state_root_sig }; } @@ -34,6 +44,7 @@ void ISerializable.Serialize(BinaryWriter writer) { writer.Write(ValidatorIndex); writer.WriteVarBytes(InvocationScript); + writer.Write(StateRootSignature); } } } diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs index b082f2aeb2..e0b5bdc91d 100644 --- a/src/neo/Consensus/RecoveryMessage.cs +++ b/src/neo/Consensus/RecoveryMessage.cs @@ -120,7 +120,8 @@ internal ConsensusPayload[] GetPrepareResponsePayloads(ConsensusContext context, ConsensusMessage = new PrepareResponse { ViewNumber = ViewNumber, - PreparationHash = preparationHash + PreparationHash = preparationHash, + StateRootSignature = p.StateRootSignature }, Witness = new Witness { diff --git a/src/neo/Ledger/Blockchain.state.cs b/src/neo/Ledger/Blockchain.state.cs new file mode 100644 index 0000000000..9c1a7eb479 --- /dev/null +++ b/src/neo/Ledger/Blockchain.state.cs @@ -0,0 +1,12 @@ +using Akka.Actor; + +namespace Neo.Ledger +{ + public sealed partial class Blockchain : UntypedActor + { + public UInt256 GetLocalStateRoot(uint index) + { + return UInt256.Zero; + } + } +} diff --git a/src/neo/Network/P2P/Payloads/StateRoot.cs b/src/neo/Network/P2P/Payloads/StateRoot.cs index e6da3b7e9e..dd2bcba374 100644 --- a/src/neo/Network/P2P/Payloads/StateRoot.cs +++ b/src/neo/Network/P2P/Payloads/StateRoot.cs @@ -47,10 +47,10 @@ Witness[] IVerifiable.Witnesses } public int Size => - sizeof(byte) + //Version - sizeof(uint) + //Index - UInt256.Length + //Root - Witness.Size; //Witness + sizeof(byte) + //Version + sizeof(uint) + //Index + UInt256.Length + //RootHash + Witness.Size; //Witness StateRoot ICloneable.Clone() { diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index d756843a2f..66b316026f 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -190,6 +190,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm // Forcing hashes to 0 because mempool is currently shared ppToSend.TransactionHashes = new UInt256[0]; ppToSend.TransactionHashes.Length.Should().Be(0); + prepReq.Data = ppToSend.ToArray(); Console.WriteLine($"\nAsserting PreparationPayloads is 1 (After MakePrepareRequest)..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(1); mockContext.Object.PreparationPayloads[prepReq.ValidatorIndex] = null; @@ -206,9 +207,27 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(6); Console.WriteLine("\nFailed because it is not primary and it created the prereq...Time to adjust"); - prepReq.ValidatorIndex = 1; //simulating primary as prepreq creator (signature is skip, no problem) + var stateRootData = mockContext.Object.EnsureStateRoot().GetHashData(); + prepReq = GetPrepareRequestAndSignStateRoot(prepReq, 1, kp_array[1], stateRootData); + //prepReq.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; + 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 + }; + mockContext.Object.GetPrimaryIndex(mockContext.Object.ViewNumber).Should().Be(1); + mockContext.Object.MyIndex.Should().Be(0); + Console.WriteLine($"\nAsserting tx count is 0..."); + prepReq.GetDeserializedMessage().TransactionHashes.Count().Should().Be(0); TellConsensusPayload(actorConsensus, prepReq); var OnPrepResponse = subscriber.ExpectMsg(); @@ -219,9 +238,13 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(2); Console.WriteLine($"\nAsserting CountFailed is 5..."); mockContext.Object.CountFailed.Should().Be(5); + Console.WriteLine("\nAsserting PrepareResponse ValidatorIndex is 0..."); + prepResponsePayload.ValidatorIndex.Should().Be(0); + // Using correct signed response to replace prepareresponse sent + mockContext.Object.PreparationPayloads[prepResponsePayload.ValidatorIndex] = GetPrepareResponsePayloadAndSignStateRoot(prepResponsePayload, 0, kp_array[0], stateRootData); // Simulating CN 3 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 2)); + TellConsensusPayload(actorConsensus, GetPrepareResponsePayloadAndSignStateRoot(prepResponsePayload, 2, kp_array[2], stateRootData)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; @@ -234,7 +257,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(4); // Simulating CN 5 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 4)); + TellConsensusPayload(actorConsensus, GetPrepareResponsePayloadAndSignStateRoot(prepResponsePayload, 4, kp_array[4], stateRootData)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; @@ -245,10 +268,20 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(4); Console.WriteLine($"\nAsserting CountFailed is 3..."); mockContext.Object.CountFailed.Should().Be(3); + var updatedContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators); + // Mock StateRoot to use mock Validators to sign + var root = mockContext.Object.EnsureStateRoot(); + var mockRoot = new Mock(); + mockRoot.Object.Version = root.Version; + mockRoot.Object.Index = root.Index; + mockRoot.Object.RootHash = root.RootHash; + mockRoot.Setup(p => p.GetScriptHashesForVerifying(It.IsAny())).Returns(p => new UInt160[] { updatedContract.ScriptHash }); + mockContext.Object.PreviousBlockStateRoot = mockRoot.Object; // Simulating CN 4 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 3)); + TellConsensusPayload(actorConsensus, GetPrepareResponsePayloadAndSignStateRoot(prepResponsePayload, 3, kp_array[3], stateRootData)); var onCommitPayload = subscriber.ExpectMsg(); + var onStateRoot = subscriber.ExpectMsg(); var commitPayload = (ConsensusPayload)onCommitPayload.Inventory; Commit cm = (Commit)commitPayload.ConsensusMessage; Console.WriteLine("\nAsserting PreparationPayloads count is 5..."); @@ -261,18 +294,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm 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(); @@ -282,7 +303,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm } 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}"); // =============================================================== @@ -316,6 +336,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("\n=========================="); Console.WriteLine("\nCN7 simulation time"); + TellConsensusPayload(actorConsensus, cmPayloadTemp); var tempPayloadToBlockAndWait = subscriber.ExpectMsg(); var rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; @@ -473,6 +494,25 @@ public ConsensusPayload GetPayloadAndModifyValidator(ConsensusPayload cpToCopy, return cpTemp; } + public ConsensusPayload GetPrepareRequestAndSignStateRoot(ConsensusPayload req, ushort vI, KeyPair kp, byte[] stateRootData) + { + var tmp = req.ToArray().AsSerializable(); + tmp.ValidatorIndex = vI; + var message = tmp.GetDeserializedMessage(); + message.StateRootSignature = Crypto.Sign(stateRootData, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); + tmp.ConsensusMessage = message; + return tmp; + } + public ConsensusPayload GetPrepareResponsePayloadAndSignStateRoot(ConsensusPayload resp, ushort vI, KeyPair kp, byte[] stateRootData) + { + var tmp = resp.ToArray().AsSerializable(); + tmp.ValidatorIndex = vI; + var message = tmp.GetDeserializedMessage(); + message.StateRootSignature = Crypto.Sign(stateRootData, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); + tmp.ConsensusMessage = message; + return tmp; + } + private void SignPayload(ConsensusPayload payload, KeyPair kp) { ContractParametersContext sc; @@ -537,13 +577,14 @@ public void TestSerializeAndDeserializeConsensusContext() var prepareRequestMessage = new PrepareRequest { TransactionHashes = consensusContext.TransactionHashes, - Timestamp = 23 + Timestamp = 23, + StateRootSignature = new byte[64] }; 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[0] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash, StateRootSignature = new byte[64] }, 0, new[] { (byte)'t', (byte)'e' }); + consensusContext.PreparationPayloads[1] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash, StateRootSignature = new byte[64] }, 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[3] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash, StateRootSignature = new byte[64] }, 3, new[] { (byte)'1', (byte)'2' }); consensusContext.PreparationPayloads[4] = null; consensusContext.PreparationPayloads[5] = null; @@ -641,7 +682,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepar new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' } + InvocationScript = new[] { (byte)'t', (byte)'e' }, + StateRootSignature = new byte[64] } }, { @@ -649,7 +691,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepar new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' } + InvocationScript = new[] { (byte)'1', (byte)'2' }, + StateRootSignature = new byte[64] } }, { @@ -657,7 +700,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepar new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 6, - InvocationScript = new[] { (byte)'3', (byte)'!' } + InvocationScript = new[] { (byte)'3', (byte)'!' }, + StateRootSignature = new byte[64] } } }, @@ -731,6 +775,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR }, PrepareRequestMessage = new PrepareRequest { + StateRootSignature = new byte[64], TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })), @@ -741,7 +786,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' } + InvocationScript = new[] { (byte)'t', (byte)'e' }, + StateRootSignature = new byte[64], } }, { @@ -749,7 +795,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 1, - InvocationScript = new[] { (byte)'s', (byte)'t' } + InvocationScript = new[] { (byte)'s', (byte)'t' }, + StateRootSignature = new byte[64], } }, { @@ -757,7 +804,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' } + InvocationScript = new[] { (byte)'1', (byte)'2' }, + StateRootSignature = new byte[64], } } }, @@ -784,6 +832,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC ChangeViewMessages = new Dictionary(), PrepareRequestMessage = new PrepareRequest { + StateRootSignature = new byte[64], TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationMessages = new Dictionary() @@ -793,7 +842,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' } + InvocationScript = new[] { (byte)'t', (byte)'e' }, + StateRootSignature = new byte[64] } }, { @@ -801,7 +851,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 1, - InvocationScript = new[] { (byte)'s', (byte)'t' } + InvocationScript = new[] { (byte)'s', (byte)'t' }, + StateRootSignature = new byte[64] } }, { @@ -809,7 +860,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' } + InvocationScript = new[] { (byte)'1', (byte)'2' }, + StateRootSignature = new byte[64] } }, { @@ -817,7 +869,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 6, - InvocationScript = new[] { (byte)'3', (byte)'!' } + InvocationScript = new[] { (byte)'3', (byte)'!' }, + StateRootSignature = new byte[64] } } }, @@ -844,6 +897,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm ChangeViewMessages = new Dictionary(), PrepareRequestMessage = new PrepareRequest { + StateRootSignature = new byte[64], TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationMessages = new Dictionary() @@ -853,7 +907,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' } + InvocationScript = new[] { (byte)'t', (byte)'e' }, + StateRootSignature = new byte[64], } }, { @@ -861,7 +916,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 1, - InvocationScript = new[] { (byte)'s', (byte)'t' } + InvocationScript = new[] { (byte)'s', (byte)'t' }, + StateRootSignature = new byte[64], } }, { @@ -869,7 +925,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' } + InvocationScript = new[] { (byte)'1', (byte)'2' }, + StateRootSignature = new byte[64], } }, { @@ -877,7 +934,8 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 6, - InvocationScript = new[] { (byte)'3', (byte)'!' } + InvocationScript = new[] { (byte)'3', (byte)'!' }, + StateRootSignature = new byte[64], } } }, From 0d37d7efbfdae2bbf3705941a6d584f476f2ba07 Mon Sep 17 00:00:00 2001 From: ZhangTao Date: Wed, 15 Jul 2020 16:01:01 +0800 Subject: [PATCH 03/19] [neox] network (#1722) * network * format * add GetStateRootPayload * recover ping * rm state height in Update * get state root of Blockchain.Height - 1 * rm Blockchain.State.cs --- src/neo/Ledger/Blockchain.cs | 2 ++ src/neo/Network/P2P/MessageCommand.cs | 4 ++- .../P2P/Payloads/GetStateRootPayload.cs | 30 +++++++++++++++++++ .../Network/P2P/RemoteNode.ProtocolHandler.cs | 21 +++++++++++++ src/neo/Network/P2P/TaskManager.cs | 15 ++++++++++ 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 src/neo/Network/P2P/Payloads/GetStateRootPayload.cs diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 4e545d72e8..3e56c4be77 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -72,6 +72,8 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu public uint HeaderHeight => currentSnapshot.HeaderHeight; public UInt256 CurrentBlockHash => currentSnapshot.CurrentBlockHash; public UInt256 CurrentHeaderHash => currentSnapshot.CurrentHeaderHash; + public StateRoot LatestValidatorsStateRoot => new StateRoot(); + public long StateHeight => -1; private static Blockchain singleton; public static Blockchain Singleton diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index 1c3ce68659..c47fae11ca 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -42,7 +42,8 @@ public enum MessageCommand : byte Consensus = 0x2d, [ReflectionCache(typeof(StateRoot))] StateRoot = 0x2e, - Reject = 0x2f, + [ReflectionCache(typeof(GetStateRootPayload))] + GetStateRoot = 0x2f, //SPV protocol [ReflectionCache(typeof(FilterLoadPayload))] @@ -55,5 +56,6 @@ public enum MessageCommand : byte //others Alert = 0x40, + Reject = 0x41, } } diff --git a/src/neo/Network/P2P/Payloads/GetStateRootPayload.cs b/src/neo/Network/P2P/Payloads/GetStateRootPayload.cs new file mode 100644 index 0000000000..94af7b4e05 --- /dev/null +++ b/src/neo/Network/P2P/Payloads/GetStateRootPayload.cs @@ -0,0 +1,30 @@ +using Neo.IO; +using System; +using System.IO; + +namespace Neo.Network.P2P.Payloads +{ + public class GetStateRootPayload : ISerializable + { + public uint Index; + public int Size => sizeof(uint); + + public static GetStateRootPayload Create(uint index) + { + return new GetStateRootPayload + { + Index = index + }; + } + + void ISerializable.Deserialize(BinaryReader reader) + { + Index = reader.ReadUInt32(); + } + + void ISerializable.Serialize(BinaryWriter writer) + { + writer.Write(Index); + } + } +} diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 530b777601..17d2c14f4e 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -107,6 +107,12 @@ private void OnMessage(Message msg) if (msg.Payload.Size <= Transaction.MaxTransactionSize) OnInventoryReceived((Transaction)msg.Payload); break; + case MessageCommand.StateRoot: + OnRootReceived((StateRoot)msg.Payload); + break; + case MessageCommand.GetStateRoot: + OnGetRootReceived((GetStateRootPayload)msg.Payload); + break; case MessageCommand.Verack: case MessageCommand.Version: throw new ProtocolViolationException(); @@ -119,6 +125,21 @@ private void OnMessage(Message msg) } } + private void OnGetRootReceived(GetStateRootPayload payload) + { + var state_root = Blockchain.Singleton.LatestValidatorsStateRoot; + if (state_root is null) return; + if (state_root.Index < payload.Index) return; + EnqueueMessage(Message.Create(MessageCommand.StateRoot, state_root)); + } + + private void OnRootReceived(StateRoot payload) + { + if (knownHashes.Contains(payload.Hash)) return; + system.Blockchain.Tell(payload); + knownHashes.Add(payload.Hash); + } + private void OnAddrMessageReceived(AddrPayload payload) { system.LocalNode.Tell(new Peer.Peers diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 5bea005a36..cddcbdc7c0 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -295,6 +295,21 @@ private void RequestTasks() if (lastTaskIndex >= highestBlockIndex) break; if (!AssignSyncTask(++lastTaskIndex)) break; } + + SyncStateRoot(); + } + + private void SyncStateRoot() + { + if (Blockchain.Singleton.StateHeight + 1 < Blockchain.Singleton.Height) + { + var session = sessions.Where(p => Blockchain.Singleton.StateHeight <= p.Value.LastBlockIndex) + .OrderByDescending(p => p.Value.LastBlockIndex) + .ThenBy(p => p.Value.IndexTasks.Count) + .FirstOrDefault(); + if (session.Value != null) + session.Key.Tell(Message.Create(MessageCommand.GetStateRoot, GetStateRootPayload.Create(Blockchain.Singleton.Height - 1))); + } } private void SendPingMessage() From 3474465f6697aa0869e1f75020806a4f639202ea Mon Sep 17 00:00:00 2001 From: ZhangTao Date: Thu, 16 Jul 2020 15:52:12 +0800 Subject: [PATCH 04/19] [neox] Changes in Ledger (#1737) * cn state root verify and persist * check state root cache * remove mpt proof, move to plugin * TryAdd optimization * fix * .. * file name * filename * fix error Co-authored-by: Shargon --- src/neo/Ledger/Blockchain.State.cs | 75 ++++++++++++++++++++++++++++++ src/neo/Ledger/Blockchain.cs | 3 +- src/neo/Ledger/Blockchain.state.cs | 12 ----- 3 files changed, 76 insertions(+), 14 deletions(-) create mode 100644 src/neo/Ledger/Blockchain.State.cs delete mode 100644 src/neo/Ledger/Blockchain.state.cs diff --git a/src/neo/Ledger/Blockchain.State.cs b/src/neo/Ledger/Blockchain.State.cs new file mode 100644 index 0000000000..d4a236ca9e --- /dev/null +++ b/src/neo/Ledger/Blockchain.State.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using Akka.Actor; +using Neo.Cryptography.MPT; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; + +namespace Neo.Ledger +{ + public sealed partial class Blockchain : UntypedActor + { + public StateRoot LatestValidatorsStateRoot + { + get + { + var state_root = currentSnapshot.ValidatorsStateRoot.Get(); + return state_root.RootHash is null ? null : state_root; + } + } + public long StateHeight => LatestValidatorsStateRoot is null ? -1 : (long)LatestValidatorsStateRoot.Index; + + private readonly int MaxStateRootCacheCount = 100; + private Dictionary state_root_cache = new Dictionary(); + + public UInt256 GetLocalStateRoot(uint index) + { + return currentSnapshot.LocalStateRoot.TryGet(index)?.Hash; + } + + private VerifyResult OnNewStateRoot(StateRoot root) + { + if (LatestValidatorsStateRoot != null && root.Index <= LatestValidatorsStateRoot.Index) return VerifyResult.AlreadyExists; + if (Height + MaxStateRootCacheCount < root.Index) return VerifyResult.Invalid; + if (Height < root.Index && root.Index <= Height + MaxStateRootCacheCount) + { + if (!state_root_cache.TryAdd(root.Index, root)) return VerifyResult.AlreadyExists; + return VerifyResult.Succeed; + } + var result = PersistCnStateRoot(root); + if (result == VerifyResult.Succeed && Height < root.Index + 2) + system.LocalNode.Tell(new LocalNode.SendDirectly { Inventory = root }); + return result; + } + + private VerifyResult PersistCnStateRoot(StateRoot root) + { + if (!root.Verify(currentSnapshot)) return VerifyResult.Invalid; + if (currentSnapshot.LocalStateRoot.TryGet(root.Index)?.Hash == root.RootHash) + { + using (SnapshotView snapshot = GetSnapshot()) + { + var confirmedRoot = snapshot.ValidatorsStateRoot.GetAndChange(); + confirmedRoot.Version = root.Version; + confirmedRoot.Index = root.Index; + confirmedRoot.RootHash = root.RootHash; + confirmedRoot.Witness = root.Witness; + snapshot.Commit(); + } + UpdateCurrentSnapshot(); + return VerifyResult.Succeed; + } + return VerifyResult.Invalid; + } + + private void CheckStateRootCache() + { + var index = Height - 1; + if (0 <= index && state_root_cache.TryGetValue(index, out StateRoot root)) + { + state_root_cache.Remove(index); + OnNewStateRoot(root); + } + } + } +} diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 3e56c4be77..6e97bd300c 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -72,8 +72,6 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu public uint HeaderHeight => currentSnapshot.HeaderHeight; public UInt256 CurrentBlockHash => currentSnapshot.CurrentBlockHash; public UInt256 CurrentHeaderHash => currentSnapshot.CurrentHeaderHash; - public StateRoot LatestValidatorsStateRoot => new StateRoot(); - public long StateHeight => -1; private static Blockchain singleton; public static Blockchain Singleton @@ -470,6 +468,7 @@ private void Persist(Block block) if (commitExceptions != null) throw new AggregateException(commitExceptions); } UpdateCurrentSnapshot(); + CheckStateRootCache(); block_cache.Remove(block.PrevHash); MemPool.UpdatePoolForBlockPersisted(block, currentSnapshot); Context.System.EventStream.Publish(new PersistCompleted { Block = block }); diff --git a/src/neo/Ledger/Blockchain.state.cs b/src/neo/Ledger/Blockchain.state.cs deleted file mode 100644 index 9c1a7eb479..0000000000 --- a/src/neo/Ledger/Blockchain.state.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Akka.Actor; - -namespace Neo.Ledger -{ - public sealed partial class Blockchain : UntypedActor - { - public UInt256 GetLocalStateRoot(uint index) - { - return UInt256.Zero; - } - } -} From 0c272919bb6ce0cdc08dd131df2bfee2c01e42e5 Mon Sep 17 00:00:00 2001 From: ZhangTao Date: Tue, 21 Jul 2020 16:00:47 +0800 Subject: [PATCH 05/19] relay when state root update (#1778) --- src/neo/Consensus/ConsensusContext.cs | 2 +- src/neo/Ledger/Blockchain.State.cs | 2 ++ src/neo/Network/P2P/Payloads/StateRoot.cs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index 8eaa9a56df..646656b824 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -174,7 +174,7 @@ public StateRoot EnsureStateRoot() { PreviousBlockStateRoot = new StateRoot { - Version = 0, + Version = StateRoot.CurrentVersion, Index = Block.Index - 1, RootHash = Blockchain.Singleton.GetLocalStateRoot(Block.Index - 1) }; diff --git a/src/neo/Ledger/Blockchain.State.cs b/src/neo/Ledger/Blockchain.State.cs index d4a236ca9e..1ea59a3a17 100644 --- a/src/neo/Ledger/Blockchain.State.cs +++ b/src/neo/Ledger/Blockchain.State.cs @@ -59,6 +59,8 @@ private VerifyResult PersistCnStateRoot(StateRoot root) UpdateCurrentSnapshot(); return VerifyResult.Succeed; } + if (StateRoot.CurrentVersion < root.Version && Height < root.Index + 2) + system.LocalNode.Tell(new LocalNode.SendDirectly { Inventory = root }); return VerifyResult.Invalid; } diff --git a/src/neo/Network/P2P/Payloads/StateRoot.cs b/src/neo/Network/P2P/Payloads/StateRoot.cs index dd2bcba374..a2efedb0f7 100644 --- a/src/neo/Network/P2P/Payloads/StateRoot.cs +++ b/src/neo/Network/P2P/Payloads/StateRoot.cs @@ -12,6 +12,7 @@ namespace Neo.Network.P2P.Payloads { public class StateRoot : ICloneable, IInventory { + public static readonly byte CurrentVersion = 0; public byte Version; public uint Index; public UInt256 RootHash; From 8c2bcea438e806f2ba04c7eaae7cf93fe5470b92 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 24 Jul 2020 10:50:17 +0800 Subject: [PATCH 06/19] impl SeekInternal replace FindInternal --- src/neo/Persistence/MPTDataCache.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/neo/Persistence/MPTDataCache.cs b/src/neo/Persistence/MPTDataCache.cs index 7463d58859..726c36fc09 100644 --- a/src/neo/Persistence/MPTDataCache.cs +++ b/src/neo/Persistence/MPTDataCache.cs @@ -4,6 +4,7 @@ using Neo.IO.Caching; using System; using System.Collections.Generic; +using System.Linq; namespace Neo.Persistence { @@ -30,9 +31,10 @@ protected override void DeleteInternal(TKey key) mptTrie.Delete(key); } - protected override IEnumerable<(TKey Key, TValue Value)> FindInternal(byte[] key_prefix) + protected override IEnumerable<(TKey, TValue)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction) { - return mptTrie.Find(key_prefix); + ByteArrayComparer comparer = direction == SeekDirection.Forward ? ByteArrayComparer.Default : ByteArrayComparer.Reverse; + return mptTrie.Find(keyOrPrefix).OrderBy(p => p.Key.ToArray(), comparer); } protected override TValue GetInternal(TKey key) From e63e0d6e7cf503261ce72dfc065f4d676c3eb494 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 11 Aug 2020 16:59:39 +0800 Subject: [PATCH 07/19] fix ut --- tests/neo.UnitTests/Persistence/UT_ReadOnlyView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neo.UnitTests/Persistence/UT_ReadOnlyView.cs b/tests/neo.UnitTests/Persistence/UT_ReadOnlyView.cs index 40a0fe8a70..19111561f7 100644 --- a/tests/neo.UnitTests/Persistence/UT_ReadOnlyView.cs +++ b/tests/neo.UnitTests/Persistence/UT_ReadOnlyView.cs @@ -18,7 +18,7 @@ public void CommitException() [TestMethod] public void Stores() { - var r = new ReadOnlyView(new MemoryStore()); + var r = new ReadOnlyView(new MemoryStore().GetSnapshot()); Assert.AreEqual(uint.MaxValue, r.BlockHashIndex.Get().Index); Assert.AreEqual(UInt256.Zero, r.BlockHashIndex.Get().Hash); From 4bcb4a849be38c8ee3bb03ca299d58b78d035dd6 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 12 Aug 2020 18:01:45 +0800 Subject: [PATCH 08/19] signature fixed size 64 --- src/neo/Consensus/Commit.cs | 2 +- src/neo/Consensus/PrepareRequest.cs | 2 +- src/neo/Consensus/PrepareResponse.cs | 2 +- src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs | 2 +- src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/neo/Consensus/Commit.cs b/src/neo/Consensus/Commit.cs index fce9b8caaf..0751a2ad8b 100644 --- a/src/neo/Consensus/Commit.cs +++ b/src/neo/Consensus/Commit.cs @@ -7,7 +7,7 @@ public class Commit : ConsensusMessage { public byte[] Signature; - public override int Size => base.Size + Signature.Length; + public override int Size => base.Size + 64; public Commit() : base(ConsensusMessageType.Commit) { } diff --git a/src/neo/Consensus/PrepareRequest.cs b/src/neo/Consensus/PrepareRequest.cs index 659c5ff795..1c7f67f584 100644 --- a/src/neo/Consensus/PrepareRequest.cs +++ b/src/neo/Consensus/PrepareRequest.cs @@ -17,7 +17,7 @@ public class PrepareRequest : ConsensusMessage + sizeof(ulong) //Timestamp + sizeof(ulong) //Nonce + TransactionHashes.GetVarSize() //TransactionHashes - + StateRootSignature.Length; //StateRootSignature + + 64; //StateRootSignature public PrepareRequest() : base(ConsensusMessageType.PrepareRequest) diff --git a/src/neo/Consensus/PrepareResponse.cs b/src/neo/Consensus/PrepareResponse.cs index b1628650f6..a7d9564f26 100644 --- a/src/neo/Consensus/PrepareResponse.cs +++ b/src/neo/Consensus/PrepareResponse.cs @@ -8,7 +8,7 @@ public class PrepareResponse : ConsensusMessage public UInt256 PreparationHash; public byte[] StateRootSignature; - public override int Size => base.Size + PreparationHash.Size + StateRootSignature.Length; + public override int Size => base.Size + PreparationHash.Size + 64; public PrepareResponse() : base(ConsensusMessageType.PrepareResponse) diff --git a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs index 5d783c6796..0aa1cf2b71 100644 --- a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs @@ -16,7 +16,7 @@ public class CommitPayloadCompact : ISerializable int ISerializable.Size => sizeof(byte) + //ViewNumber sizeof(ushort) + //ValidatorIndex - Signature.Length + //Signature + 64 + //Signature InvocationScript.GetVarSize(); //InvocationScript void ISerializable.Deserialize(BinaryReader reader) diff --git a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs index ceac4563fc..fff54e6810 100644 --- a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs @@ -16,7 +16,7 @@ public class PreparationPayloadCompact : ISerializable int ISerializable.Size => sizeof(ushort) + //ValidatorIndex InvocationScript.GetVarSize() + //InvocationScript - StateRootSignature.Length; //StateRootSignature + 64; //StateRootSignature void ISerializable.Deserialize(BinaryReader reader) { From e3de92cbb49630b984a87b2ff2082d7dbad32fd1 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 12 Aug 2020 18:03:50 +0800 Subject: [PATCH 09/19] fix --- src/neo/Consensus/PrepareResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Consensus/PrepareResponse.cs b/src/neo/Consensus/PrepareResponse.cs index a7d9564f26..31a06b92b9 100644 --- a/src/neo/Consensus/PrepareResponse.cs +++ b/src/neo/Consensus/PrepareResponse.cs @@ -8,7 +8,7 @@ public class PrepareResponse : ConsensusMessage public UInt256 PreparationHash; public byte[] StateRootSignature; - public override int Size => base.Size + PreparationHash.Size + 64; + public override int Size => base.Size + UInt256.Size + 64; public PrepareResponse() : base(ConsensusMessageType.PrepareResponse) From f18a1b39cc3643dfdff90b011621d57ff1af26f4 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 12 Aug 2020 18:08:31 +0800 Subject: [PATCH 10/19] fix uint zero minus --- src/neo/Ledger/Blockchain.State.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Ledger/Blockchain.State.cs b/src/neo/Ledger/Blockchain.State.cs index 1ea59a3a17..a6049aa379 100644 --- a/src/neo/Ledger/Blockchain.State.cs +++ b/src/neo/Ledger/Blockchain.State.cs @@ -66,7 +66,7 @@ private VerifyResult PersistCnStateRoot(StateRoot root) private void CheckStateRootCache() { - var index = Height - 1; + var index = (long)Height - 1; if (0 <= index && state_root_cache.TryGetValue(index, out StateRoot root)) { state_root_cache.Remove(index); From acee19a2cd68e741fa988b105aaa1fd91d5f6bc2 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 13 Aug 2020 09:36:08 +0800 Subject: [PATCH 11/19] fix --- src/neo/Consensus/PrepareResponse.cs | 2 +- src/neo/Ledger/Blockchain.State.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/Consensus/PrepareResponse.cs b/src/neo/Consensus/PrepareResponse.cs index 31a06b92b9..1a584344d9 100644 --- a/src/neo/Consensus/PrepareResponse.cs +++ b/src/neo/Consensus/PrepareResponse.cs @@ -8,7 +8,7 @@ public class PrepareResponse : ConsensusMessage public UInt256 PreparationHash; public byte[] StateRootSignature; - public override int Size => base.Size + UInt256.Size + 64; + public override int Size => base.Size + UInt256.Length + 64; public PrepareResponse() : base(ConsensusMessageType.PrepareResponse) diff --git a/src/neo/Ledger/Blockchain.State.cs b/src/neo/Ledger/Blockchain.State.cs index a6049aa379..d999b2a0ee 100644 --- a/src/neo/Ledger/Blockchain.State.cs +++ b/src/neo/Ledger/Blockchain.State.cs @@ -67,9 +67,9 @@ private VerifyResult PersistCnStateRoot(StateRoot root) private void CheckStateRootCache() { var index = (long)Height - 1; - if (0 <= index && state_root_cache.TryGetValue(index, out StateRoot root)) + if (0 <= index && state_root_cache.TryGetValue((uint)index, out StateRoot root)) { - state_root_cache.Remove(index); + state_root_cache.Remove((uint)index); OnNewStateRoot(root); } } From 0b6be923c591a2f4d8661c2982a293cf9de67772 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 13 Aug 2020 10:38:59 +0800 Subject: [PATCH 12/19] fix ut --- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 66b316026f..8624a6b248 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -214,6 +214,15 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.PreparationPayloads[mockContext.Object.MyIndex] = null; for (int i = 0; i < mockContext.Object.Validators.Length; i++) Console.WriteLine($"{mockContext.Object.Validators[i]}/{Contract.CreateSignatureContract(mockContext.Object.Validators[i]).ScriptHash}"); + var last_seen_message = new int[7]; + for (int i = 0; i < 7; i++) + { + if (mockContext.Object.LastSeenMessage.TryGetValue(mockContext.Object.Validators[i], out int value)) + { + last_seen_message[i] = value; + } + mockContext.Object.LastSeenMessage.Remove(mockContext.Object.Validators[i]); + } mockContext.Object.Validators = new ECPoint[7] { kp_array[0].PublicKey, @@ -224,6 +233,10 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm kp_array[5].PublicKey, kp_array[6].PublicKey }; + for (int i = 0; i < 7; i++) + { + mockContext.Object.LastSeenMessage[mockContext.Object.Validators[i]] = last_seen_message[i]; + } mockContext.Object.GetPrimaryIndex(mockContext.Object.ViewNumber).Should().Be(1); mockContext.Object.MyIndex.Should().Be(0); Console.WriteLine($"\nAsserting tx count is 0..."); From ac8d8ec730d6978e7fb894ba117de373451ec1b2 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 8 Sep 2020 11:14:42 +0800 Subject: [PATCH 13/19] add ContainsInternal in MPTDataCache --- src/neo/Persistence/MPTDataCache.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/neo/Persistence/MPTDataCache.cs b/src/neo/Persistence/MPTDataCache.cs index 726c36fc09..eaf96a5063 100644 --- a/src/neo/Persistence/MPTDataCache.cs +++ b/src/neo/Persistence/MPTDataCache.cs @@ -37,6 +37,11 @@ protected override void DeleteInternal(TKey key) return mptTrie.Find(keyOrPrefix).OrderBy(p => p.Key.ToArray(), comparer); } + protected override bool ContainsInternal(TKey key) + { + return mptTrie[key] is null ? false : true; + } + protected override TValue GetInternal(TKey key) { return mptTrie[key]; From f62a8f2108c4d9d9acd1b55c17bf8a4e12023abe Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 8 Sep 2020 11:22:05 +0800 Subject: [PATCH 14/19] fix build error --- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 8 ++++---- .../SmartContract/Native/UT_PolicyContract.cs | 9 --------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index d52306ef4f..98902c04f2 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -210,10 +210,10 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.PreparationPayloads[mockContext.Object.MyIndex] = null; for (int i = 0; i < mockContext.Object.Validators.Length; i++) Console.WriteLine($"{mockContext.Object.Validators[i]}/{Contract.CreateSignatureContract(mockContext.Object.Validators[i]).ScriptHash}"); - var last_seen_message = new int[7]; + var last_seen_message = new uint[7]; for (int i = 0; i < 7; i++) { - if (mockContext.Object.LastSeenMessage.TryGetValue(mockContext.Object.Validators[i], out int value)) + if (mockContext.Object.LastSeenMessage.TryGetValue(mockContext.Object.Validators[i], out uint value)) { last_seen_message[i] = value; } @@ -498,7 +498,7 @@ public ConsensusPayload GetPayloadAndModifyValidator(ConsensusPayload cpToCopy, public ConsensusPayload GetPrepareRequestAndSignStateRoot(ConsensusPayload req, ushort vI, KeyPair kp, byte[] stateRootData) { var tmp = req.ToArray().AsSerializable(); - tmp.ValidatorIndex = vI; + tmp.ValidatorIndex = (byte)vI; var message = tmp.GetDeserializedMessage(); message.StateRootSignature = Crypto.Sign(stateRootData, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); tmp.ConsensusMessage = message; @@ -507,7 +507,7 @@ public ConsensusPayload GetPrepareRequestAndSignStateRoot(ConsensusPayload req, public ConsensusPayload GetPrepareResponsePayloadAndSignStateRoot(ConsensusPayload resp, ushort vI, KeyPair kp, byte[] stateRootData) { var tmp = resp.ToArray().AsSerializable(); - tmp.ValidatorIndex = vI; + tmp.ValidatorIndex = (byte)vI; var message = tmp.GetDeserializedMessage(); message.StateRootSignature = Crypto.Sign(stateRootData, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); tmp.ConsensusMessage = message; diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 9cf22fae78..cd35e57ed8 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -253,15 +253,6 @@ public void Check_BlockAccount() ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); - // Check - - NativeContract.Policy.IsAnyAccountBlocked(snapshot).Should().BeFalse(); - NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Zero).Should().BeFalse(); - NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Zero, UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")).Should().BeTrue(); - NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), UInt160.Zero).Should().BeTrue(); - NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Zero, UInt160.Parse("0xb400ff00ff00ff00ff00ff00ff00ff00ff00ff01")).Should().BeTrue(); - NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Parse("0xb400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), UInt160.Zero).Should().BeTrue(); - ret = NativeContract.Policy.Call(snapshot, "getBlockedAccounts"); ret.Should().BeOfType(); (ret as VM.Types.Array).Count.Should().Be(2); From df96a372247d3ce42514a9a5bbcaabff8265e226 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 8 Sep 2020 11:56:17 +0800 Subject: [PATCH 15/19] fix ut --- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 7 ++++++- .../neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs | 6 +++--- .../SmartContract/Native/UT_PolicyContract.cs | 9 +++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 98902c04f2..8e3c338d22 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -217,6 +217,10 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm { last_seen_message[i] = value; } + else + { + last_seen_message[i] = uint.MaxValue; + } mockContext.Object.LastSeenMessage.Remove(mockContext.Object.Validators[i]); } mockContext.Object.Validators = new ECPoint[7] @@ -231,7 +235,8 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm }; for (int i = 0; i < 7; i++) { - mockContext.Object.LastSeenMessage[mockContext.Object.Validators[i]] = last_seen_message[i]; + if (last_seen_message[i] != uint.MaxValue) + mockContext.Object.LastSeenMessage[mockContext.Object.Validators[i]] = last_seen_message[i]; } mockContext.Object.GetPrimaryIndex(mockContext.Object.ViewNumber).Should().Be(1); mockContext.Object.MyIndex.Should().Be(0); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index c2e92f5564..64fe3171ee 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -1121,7 +1121,7 @@ public void Test_VerifyStateIndependent() var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction @@ -1193,7 +1193,7 @@ public void Test_VerifyStateDependent() var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction @@ -1263,7 +1263,7 @@ public void Test_Verify() var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index cd35e57ed8..9cf22fae78 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -253,6 +253,15 @@ public void Check_BlockAccount() ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); + // Check + + NativeContract.Policy.IsAnyAccountBlocked(snapshot).Should().BeFalse(); + NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Zero).Should().BeFalse(); + NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Zero, UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")).Should().BeTrue(); + NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), UInt160.Zero).Should().BeTrue(); + NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Zero, UInt160.Parse("0xb400ff00ff00ff00ff00ff00ff00ff00ff00ff01")).Should().BeTrue(); + NativeContract.Policy.IsAnyAccountBlocked(snapshot, UInt160.Parse("0xb400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), UInt160.Zero).Should().BeTrue(); + ret = NativeContract.Policy.Call(snapshot, "getBlockedAccounts"); ret.Should().BeOfType(); (ret as VM.Types.Array).Count.Should().Be(2); From 80455c2744fc58882d4e26fe0b8f0bdbb72a6413 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 9 Sep 2020 11:32:01 +0800 Subject: [PATCH 16/19] recover consensus --- src/neo/Consensus/Commit.cs | 2 +- src/neo/Consensus/ConsensusContext.cs | 50 ++---- src/neo/Consensus/ConsensusService.cs | 16 -- src/neo/Consensus/PrepareRequest.cs | 8 +- src/neo/Consensus/PrepareResponse.cs | 5 +- .../RecoveryMessage.CommitPayloadCompact.cs | 2 +- ...coveryMessage.PreparationPayloadCompact.cs | 16 +- src/neo/Consensus/RecoveryMessage.cs | 3 +- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 149 ++++-------------- 9 files changed, 53 insertions(+), 198 deletions(-) diff --git a/src/neo/Consensus/Commit.cs b/src/neo/Consensus/Commit.cs index 0751a2ad8b..fce9b8caaf 100644 --- a/src/neo/Consensus/Commit.cs +++ b/src/neo/Consensus/Commit.cs @@ -7,7 +7,7 @@ public class Commit : ConsensusMessage { public byte[] Signature; - public override int Size => base.Size + 64; + public override int Size => base.Size + Signature.Length; public Commit() : base(ConsensusMessageType.Commit) { } diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index fb5206f5b7..79182f1697 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -24,7 +24,6 @@ public class ConsensusContext : IDisposable, ISerializable private const byte ConsensusStatePrefix = 0xf4; public Block Block; - public StateRoot PreviousBlockStateRoot; public byte ViewNumber; public ECPoint[] Validators; public int MyIndex; @@ -109,31 +108,11 @@ public Block CreateBlock() sc.AddSignature(contract, Validators[i], CommitPayloads[i].GetDeserializedMessage().Signature); j++; } - if (!sc.Completed) throw new Exception("There are not enough signatures for sign the Block."); Block.Witness = sc.GetWitnesses()[0]; Block.Transactions = TransactionHashes.Select(p => Transactions[p]).ToArray(); return Block; } - public StateRoot CreateStateRoot() - { - EnsureHeader(); - Contract contract = Contract.CreateMultiSigContract(M, Validators); - ContractParametersContext sc = new ContractParametersContext(PreviousBlockStateRoot); - for (int i = 0, j = 0; i < Validators.Length && j < M; i++) - { - if (PreparationPayloads[i]?.ConsensusMessage.ViewNumber != ViewNumber) continue; - if (i == GetPrimaryIndex(ViewNumber)) - sc.AddSignature(contract, Validators[i], PreparationPayloads[i].GetDeserializedMessage().StateRootSignature); - else - sc.AddSignature(contract, Validators[i], PreparationPayloads[i].GetDeserializedMessage().StateRootSignature); - j++; - } - if (!sc.Completed) throw new Exception("There are not enough signatures for sign the state root."); - PreviousBlockStateRoot.Witness = sc.GetWitnesses()[0]; - return PreviousBlockStateRoot; - } - public void Deserialize(BinaryReader reader) { Reset(0); @@ -175,20 +154,6 @@ public Block EnsureHeader() return Block; } - public StateRoot EnsureStateRoot() - { - if (PreviousBlockStateRoot is null) - { - PreviousBlockStateRoot = new StateRoot - { - Version = StateRoot.CurrentVersion, - Index = Block.Index - 1, - RootHash = Blockchain.Singleton.GetLocalStateRoot(Block.Index - 1) - }; - } - return PreviousBlockStateRoot; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte GetPrimaryIndex(byte viewNumber) { @@ -356,8 +321,7 @@ public ConsensusPayload MakePrepareRequest() { Timestamp = Block.Timestamp, Nonce = Block.ConsensusData.Nonce, - TransactionHashes = TransactionHashes, - StateRootSignature = EnsureStateRoot().Sign(keyPair) + TransactionHashes = TransactionHashes }); } @@ -374,7 +338,13 @@ public ConsensusPayload MakeRecoveryMessage() PrepareRequest prepareRequestMessage = null; if (TransactionHashes != null) { - prepareRequestMessage = PreparationPayloads[GetPrimaryIndex(ViewNumber)].GetDeserializedMessage(); + prepareRequestMessage = new PrepareRequest + { + ViewNumber = ViewNumber, + Timestamp = Block.Timestamp, + Nonce = Block.ConsensusData.Nonce, + TransactionHashes = TransactionHashes + }; } return MakeSignedPayload(new RecoveryMessage() { @@ -393,8 +363,7 @@ public ConsensusPayload MakePrepareResponse() { return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareResponse { - PreparationHash = PreparationPayloads[Block.ConsensusData.PrimaryIndex].Hash, - StateRootSignature = EnsureStateRoot().Sign(keyPair) + PreparationHash = PreparationPayloads[Block.ConsensusData.PrimaryIndex].Hash }); } @@ -453,7 +422,6 @@ public void Reset(byte viewNumber) keyPair = account.GetKey(); break; } - PreviousBlockStateRoot = null; } else { diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index f5f11a41ed..8054147eb5 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -169,10 +169,6 @@ private void CheckPreparations() 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)); - - StateRoot stateRoot = context.CreateStateRoot(); - Log($"relay state root, index={stateRoot.Index}, root_hash={stateRoot.RootHash}"); - blockchain.Tell(stateRoot); CheckCommits(); } } @@ -432,12 +428,6 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m Log($"Invalid request: transaction already exists", LogLevel.Warning); return; } - var stateRootHashData = context.EnsureStateRoot().GetHashData(); - if (!Crypto.VerifySignature(stateRootHashData, message.StateRootSignature, context.Validators[payload.ValidatorIndex])) - { - Log($"Invalid request: invalid state root signature", 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) @@ -500,12 +490,6 @@ private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse if (context.PreparationPayloads[payload.ValidatorIndex] != null || context.NotAcceptingPayloadsDueToViewChanging) return; if (context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex] != null && !message.PreparationHash.Equals(context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex].Hash)) return; - byte[] stateRootHashData = context.EnsureStateRoot().GetHashData(); - if (!Crypto.VerifySignature(stateRootHashData, message.StateRootSignature, context.Validators[payload.ValidatorIndex])) - { - Log($"Invalid response: invalid state root signature, height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}", LogLevel.Warning); - return; - } // Timeout extension: prepare response has been received with success // around 2*15/M=30.0/5 ~ 40% block time (for M=5) diff --git a/src/neo/Consensus/PrepareRequest.cs b/src/neo/Consensus/PrepareRequest.cs index 1c7f67f584..2369b4f9f7 100644 --- a/src/neo/Consensus/PrepareRequest.cs +++ b/src/neo/Consensus/PrepareRequest.cs @@ -11,13 +11,11 @@ public class PrepareRequest : ConsensusMessage public ulong Timestamp; public ulong Nonce; public UInt256[] TransactionHashes; - public byte[] StateRootSignature; public override int Size => base.Size - + sizeof(ulong) //Timestamp + + sizeof(ulong) //Timestamp + sizeof(ulong) //Nonce - + TransactionHashes.GetVarSize() //TransactionHashes - + 64; //StateRootSignature + + TransactionHashes.GetVarSize(); //TransactionHashes public PrepareRequest() : base(ConsensusMessageType.PrepareRequest) @@ -32,7 +30,6 @@ public override void Deserialize(BinaryReader reader) TransactionHashes = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); if (TransactionHashes.Distinct().Count() != TransactionHashes.Length) throw new FormatException(); - StateRootSignature = reader.ReadFixedBytes(64); } public override void Serialize(BinaryWriter writer) @@ -41,7 +38,6 @@ public override void Serialize(BinaryWriter writer) writer.Write(Timestamp); writer.Write(Nonce); writer.Write(TransactionHashes); - writer.Write(StateRootSignature); } } } diff --git a/src/neo/Consensus/PrepareResponse.cs b/src/neo/Consensus/PrepareResponse.cs index 1a584344d9..7c2956ccc2 100644 --- a/src/neo/Consensus/PrepareResponse.cs +++ b/src/neo/Consensus/PrepareResponse.cs @@ -6,9 +6,8 @@ namespace Neo.Consensus public class PrepareResponse : ConsensusMessage { public UInt256 PreparationHash; - public byte[] StateRootSignature; - public override int Size => base.Size + UInt256.Length + 64; + public override int Size => base.Size + PreparationHash.Size; public PrepareResponse() : base(ConsensusMessageType.PrepareResponse) @@ -19,14 +18,12 @@ public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); PreparationHash = reader.ReadSerializable(); - StateRootSignature = reader.ReadFixedBytes(64); } public override void Serialize(BinaryWriter writer) { base.Serialize(writer); writer.Write(PreparationHash); - writer.Write(StateRootSignature); } } } diff --git a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs index e8a4b7c0e7..58754691da 100644 --- a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs @@ -17,7 +17,7 @@ public class CommitPayloadCompact : ISerializable int ISerializable.Size => sizeof(byte) + //ViewNumber sizeof(byte) + //ValidatorIndex - 64 + //Signature + Signature.Length + //Signature InvocationScript.GetVarSize(); //InvocationScript void ISerializable.Deserialize(BinaryReader reader) diff --git a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs index d586b17d9d..f364c88385 100644 --- a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs @@ -11,12 +11,10 @@ public class PreparationPayloadCompact : ISerializable { public byte ValidatorIndex; public byte[] InvocationScript; - public byte[] StateRootSignature; int ISerializable.Size => - sizeof(byte) + //ValidatorIndex - InvocationScript.GetVarSize() + //InvocationScript - 64; //StateRootSignature + sizeof(byte) + //ValidatorIndex + InvocationScript.GetVarSize(); //InvocationScript void ISerializable.Deserialize(BinaryReader reader) { @@ -24,21 +22,14 @@ void ISerializable.Deserialize(BinaryReader reader) if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount) throw new FormatException(); InvocationScript = reader.ReadVarBytes(1024); - StateRootSignature = reader.ReadFixedBytes(64); } public static PreparationPayloadCompact FromPayload(ConsensusPayload payload) { - byte[] state_root_sig = Array.Empty(); - if (payload.ConsensusMessage is PrepareResponse req) - state_root_sig = req.StateRootSignature; - else if (payload.ConsensusMessage is PrepareResponse resp) - state_root_sig = resp.StateRootSignature; return new PreparationPayloadCompact { ValidatorIndex = payload.ValidatorIndex, - InvocationScript = payload.Witness.InvocationScript, - StateRootSignature = state_root_sig + InvocationScript = payload.Witness.InvocationScript }; } @@ -46,7 +37,6 @@ void ISerializable.Serialize(BinaryWriter writer) { writer.Write(ValidatorIndex); writer.WriteVarBytes(InvocationScript); - writer.Write(StateRootSignature); } } } diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs index b95403d2e8..34e2e0da9a 100644 --- a/src/neo/Consensus/RecoveryMessage.cs +++ b/src/neo/Consensus/RecoveryMessage.cs @@ -120,8 +120,7 @@ internal ConsensusPayload[] GetPrepareResponsePayloads(ConsensusContext context, ConsensusMessage = new PrepareResponse { ViewNumber = ViewNumber, - PreparationHash = preparationHash, - StateRootSignature = p.StateRootSignature + PreparationHash = preparationHash }, Witness = new Witness { diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 8e3c338d22..efbd45c5a0 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -10,7 +10,6 @@ using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests.Cryptography; @@ -186,7 +185,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm // Forcing hashes to 0 because mempool is currently shared ppToSend.TransactionHashes = new UInt256[0]; ppToSend.TransactionHashes.Length.Should().Be(0); - prepReq.Data = ppToSend.ToArray(); Console.WriteLine($"\nAsserting PreparationPayloads is 1 (After MakePrepareRequest)..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(1); mockContext.Object.PreparationPayloads[prepReq.ValidatorIndex] = null; @@ -203,45 +201,9 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(6); Console.WriteLine("\nFailed because it is not primary and it created the prereq...Time to adjust"); - var stateRootData = mockContext.Object.EnsureStateRoot().GetHashData(); - prepReq = GetPrepareRequestAndSignStateRoot(prepReq, 1, kp_array[1], stateRootData); - //prepReq.ValidatorIndex = 1; //simulating primary as prepreq creator (signature is skip, no problem) + prepReq.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; - for (int i = 0; i < mockContext.Object.Validators.Length; i++) - Console.WriteLine($"{mockContext.Object.Validators[i]}/{Contract.CreateSignatureContract(mockContext.Object.Validators[i]).ScriptHash}"); - var last_seen_message = new uint[7]; - for (int i = 0; i < 7; i++) - { - if (mockContext.Object.LastSeenMessage.TryGetValue(mockContext.Object.Validators[i], out uint value)) - { - last_seen_message[i] = value; - } - else - { - last_seen_message[i] = uint.MaxValue; - } - mockContext.Object.LastSeenMessage.Remove(mockContext.Object.Validators[i]); - } - 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 - }; - for (int i = 0; i < 7; i++) - { - if (last_seen_message[i] != uint.MaxValue) - mockContext.Object.LastSeenMessage[mockContext.Object.Validators[i]] = last_seen_message[i]; - } - mockContext.Object.GetPrimaryIndex(mockContext.Object.ViewNumber).Should().Be(1); - mockContext.Object.MyIndex.Should().Be(0); - Console.WriteLine($"\nAsserting tx count is 0..."); - prepReq.GetDeserializedMessage().TransactionHashes.Count().Should().Be(0); TellConsensusPayload(actorConsensus, prepReq); var OnPrepResponse = subscriber.ExpectMsg(); @@ -252,13 +214,9 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(2); Console.WriteLine($"\nAsserting CountFailed is 5..."); mockContext.Object.CountFailed.Should().Be(5); - Console.WriteLine("\nAsserting PrepareResponse ValidatorIndex is 0..."); - prepResponsePayload.ValidatorIndex.Should().Be(0); - // Using correct signed response to replace prepareresponse sent - mockContext.Object.PreparationPayloads[prepResponsePayload.ValidatorIndex] = GetPrepareResponsePayloadAndSignStateRoot(prepResponsePayload, 0, kp_array[0], stateRootData); // Simulating CN 3 - TellConsensusPayload(actorConsensus, GetPrepareResponsePayloadAndSignStateRoot(prepResponsePayload, 2, kp_array[2], stateRootData)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 2)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; @@ -271,7 +229,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(4); // Simulating CN 5 - TellConsensusPayload(actorConsensus, GetPrepareResponsePayloadAndSignStateRoot(prepResponsePayload, 4, kp_array[4], stateRootData)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 4)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; @@ -282,20 +240,10 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(4); Console.WriteLine($"\nAsserting CountFailed is 3..."); mockContext.Object.CountFailed.Should().Be(3); - var updatedContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators); - // Mock StateRoot to use mock Validators to sign - var root = mockContext.Object.EnsureStateRoot(); - var mockRoot = new Mock(); - mockRoot.Object.Version = root.Version; - mockRoot.Object.Index = root.Index; - mockRoot.Object.RootHash = root.RootHash; - mockRoot.Setup(p => p.GetScriptHashesForVerifying(It.IsAny())).Returns(p => new UInt160[] { updatedContract.ScriptHash }); - mockContext.Object.PreviousBlockStateRoot = mockRoot.Object; // Simulating CN 4 - TellConsensusPayload(actorConsensus, GetPrepareResponsePayloadAndSignStateRoot(prepResponsePayload, 3, kp_array[3], stateRootData)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 3)); var onCommitPayload = subscriber.ExpectMsg(); - var onStateRoot = subscriber.ExpectMsg(); var commitPayload = (ConsensusPayload)onCommitPayload.Inventory; Commit cm = (Commit)commitPayload.ConsensusMessage; Console.WriteLine("\nAsserting PreparationPayloads count is 5..."); @@ -308,11 +256,24 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm 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}"); // =============================================================== @@ -320,7 +281,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm { Value = mockContext.Object.Validators.ToByteArray() }); - mockContext.Object.Snapshot.UpdateLocalStateRoot(); mockContext.Object.Snapshot.Commit(); // =============================================================== @@ -346,7 +306,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("\n=========================="); Console.WriteLine("\nCN7 simulation time"); - TellConsensusPayload(actorConsensus, cmPayloadTemp); var tempPayloadToBlockAndWait = subscriber.ExpectMsg(); var rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; @@ -454,7 +413,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("mockContext Reset for returning Blockchain.Singleton snapshot to original state."); mockContext.Object.Reset(0); mockContext.Object.Snapshot.Storages.Delete(CreateStorageKeyForNativeNeo(14)); - mockContext.Object.Snapshot.UpdateLocalStateRoot(); mockContext.Object.Snapshot.Commit(); Console.WriteLine("mockContext Reset."); @@ -500,25 +458,6 @@ public ConsensusPayload GetPayloadAndModifyValidator(ConsensusPayload cpToCopy, return cpTemp; } - public ConsensusPayload GetPrepareRequestAndSignStateRoot(ConsensusPayload req, ushort vI, KeyPair kp, byte[] stateRootData) - { - var tmp = req.ToArray().AsSerializable(); - tmp.ValidatorIndex = (byte)vI; - var message = tmp.GetDeserializedMessage(); - message.StateRootSignature = Crypto.Sign(stateRootData, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); - tmp.ConsensusMessage = message; - return tmp; - } - public ConsensusPayload GetPrepareResponsePayloadAndSignStateRoot(ConsensusPayload resp, ushort vI, KeyPair kp, byte[] stateRootData) - { - var tmp = resp.ToArray().AsSerializable(); - tmp.ValidatorIndex = (byte)vI; - var message = tmp.GetDeserializedMessage(); - message.StateRootSignature = Crypto.Sign(stateRootData, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); - tmp.ConsensusMessage = message; - return tmp; - } - private void SignPayload(ConsensusPayload payload, KeyPair kp) { ContractParametersContext sc; @@ -583,14 +522,13 @@ public void TestSerializeAndDeserializeConsensusContext() var prepareRequestMessage = new PrepareRequest { TransactionHashes = consensusContext.TransactionHashes, - Timestamp = 23, - StateRootSignature = new byte[64] + 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, StateRootSignature = new byte[64] }, 0, new[] { (byte)'t', (byte)'e' }); - consensusContext.PreparationPayloads[1] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash, StateRootSignature = new byte[64] }, 1, new[] { (byte)'s', (byte)'t' }); + 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, StateRootSignature = new byte[64] }, 3, new[] { (byte)'1', (byte)'2' }); + 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; @@ -688,8 +626,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepar new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' }, - StateRootSignature = new byte[64] + InvocationScript = new[] { (byte)'t', (byte)'e' } } }, { @@ -697,8 +634,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepar new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' }, - StateRootSignature = new byte[64] + InvocationScript = new[] { (byte)'1', (byte)'2' } } }, { @@ -706,8 +642,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepar new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 6, - InvocationScript = new[] { (byte)'3', (byte)'!' }, - StateRootSignature = new byte[64] + InvocationScript = new[] { (byte)'3', (byte)'!' } } } }, @@ -781,7 +716,6 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR }, PrepareRequestMessage = new PrepareRequest { - StateRootSignature = new byte[64], TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })), @@ -792,8 +726,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' }, - StateRootSignature = new byte[64], + InvocationScript = new[] { (byte)'t', (byte)'e' } } }, { @@ -801,8 +734,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 1, - InvocationScript = new[] { (byte)'s', (byte)'t' }, - StateRootSignature = new byte[64], + InvocationScript = new[] { (byte)'s', (byte)'t' } } }, { @@ -810,8 +742,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' }, - StateRootSignature = new byte[64], + InvocationScript = new[] { (byte)'1', (byte)'2' } } } }, @@ -838,7 +769,6 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC ChangeViewMessages = new Dictionary(), PrepareRequestMessage = new PrepareRequest { - StateRootSignature = new byte[64], TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationMessages = new Dictionary() @@ -848,8 +778,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' }, - StateRootSignature = new byte[64] + InvocationScript = new[] { (byte)'t', (byte)'e' } } }, { @@ -857,8 +786,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 1, - InvocationScript = new[] { (byte)'s', (byte)'t' }, - StateRootSignature = new byte[64] + InvocationScript = new[] { (byte)'s', (byte)'t' } } }, { @@ -866,8 +794,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' }, - StateRootSignature = new byte[64] + InvocationScript = new[] { (byte)'1', (byte)'2' } } }, { @@ -875,8 +802,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 6, - InvocationScript = new[] { (byte)'3', (byte)'!' }, - StateRootSignature = new byte[64] + InvocationScript = new[] { (byte)'3', (byte)'!' } } } }, @@ -903,7 +829,6 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm ChangeViewMessages = new Dictionary(), PrepareRequestMessage = new PrepareRequest { - StateRootSignature = new byte[64], TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationMessages = new Dictionary() @@ -913,8 +838,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' }, - StateRootSignature = new byte[64], + InvocationScript = new[] { (byte)'t', (byte)'e' } } }, { @@ -922,8 +846,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 1, - InvocationScript = new[] { (byte)'s', (byte)'t' }, - StateRootSignature = new byte[64], + InvocationScript = new[] { (byte)'s', (byte)'t' } } }, { @@ -931,8 +854,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' }, - StateRootSignature = new byte[64], + InvocationScript = new[] { (byte)'1', (byte)'2' } } }, { @@ -940,8 +862,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm new RecoveryMessage.PreparationPayloadCompact { ValidatorIndex = 6, - InvocationScript = new[] { (byte)'3', (byte)'!' }, - StateRootSignature = new byte[64], + InvocationScript = new[] { (byte)'3', (byte)'!' } } } }, From 545b6f55696dacd7eaedf8e5b709d2c68dafdf54 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 9 Sep 2020 14:02:41 +0800 Subject: [PATCH 17/19] fix ut --- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index da8acd3dda..a65469de02 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -10,6 +10,7 @@ using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests.Cryptography; @@ -282,6 +283,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm { Value = mockContext.Object.Validators.ToByteArray() }); + mockContext.Object.Snapshot.UpdateLocalStateRoot(); mockContext.Object.Snapshot.Commit(); // =============================================================== @@ -415,6 +417,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.Reset(0); mockContext.Object.Snapshot.Storages.Delete(CreateStorageKeyForNativeNeo(14)); mockContext.Object.Snapshot.Storages.Add(CreateStorageKeyForNativeNeo(14), new StorageItem(Blockchain.StandbyValidators.OrderBy(p => p).ToArray().ToByteArray())); + mockContext.Object.Snapshot.UpdateLocalStateRoot(); mockContext.Object.Snapshot.Commit(); Console.WriteLine("mockContext Reset."); From ddea0f8d1f4f9643f4f65692116e5ae1abd2cec7 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 9 Sep 2020 14:48:40 +0800 Subject: [PATCH 18/19] Update MessageCommand.cs --- src/neo/Network/P2P/MessageCommand.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index c47fae11ca..4094fd781c 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -27,6 +27,8 @@ public enum MessageCommand : byte [ReflectionCache(typeof(GetBlocksPayload))] GetBlocks = 0x24, Mempool = 0x25, + [ReflectionCache(typeof(GetStateRootPayload))] + GetStateRoot = 0x26, [ReflectionCache(typeof(InvPayload))] Inv = 0x27, [ReflectionCache(typeof(InvPayload))] @@ -42,8 +44,7 @@ public enum MessageCommand : byte Consensus = 0x2d, [ReflectionCache(typeof(StateRoot))] StateRoot = 0x2e, - [ReflectionCache(typeof(GetStateRootPayload))] - GetStateRoot = 0x2f, + Reject = 0x2f, //SPV protocol [ReflectionCache(typeof(FilterLoadPayload))] @@ -56,6 +57,5 @@ public enum MessageCommand : byte //others Alert = 0x40, - Reject = 0x41, } } From f125152e87fa0890416c6a75ffd87566023264b2 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 9 Sep 2020 16:17:19 +0800 Subject: [PATCH 19/19] fix mpt --- src/neo/Cryptography/MPT/MPTTrie.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index 006e4280f3..df97b54f9c 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -18,7 +18,7 @@ public partial class MPTTrie public MPTTrie(ISnapshot store, UInt256 root) { this.store = store ?? throw new ArgumentNullException(); - this.root = root is null || root == UInt256.Zero ? HashNode.EmptyNode : new HashNode(root); + this.root = root is null ? HashNode.EmptyNode : new HashNode(root); } private MPTNode Resolve(HashNode n)