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 50cee9509d..2b704bca90 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -447,6 +447,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..5e7d2f57fa --- /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 Root; + 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, + Root = Root, + Witness = Witness, + }; + } + + void ICloneable.FromReplica(StateRoot replica) + { + Version = replica.Version; + Index = replica.Index; + Root = replica.Root; + 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(); + Root = 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(Root); + } + + 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"] = Root.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 86eb6bc8c2..f99f7ba8ac 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; @@ -276,6 +277,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm { Value = mockContext.Object.Validators.ToByteArray() }); + mockContext.Object.Snapshot.UpdateLocalStateRoot(); mockContext.Object.Snapshot.Commit(); // =============================================================== @@ -408,6 +410,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 014a10e6d2..7b93f34b60 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 7e04dfe229..2f107b5a48 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -119,7 +119,7 @@ public void TestValidTransaction() 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..930ac58038 --- /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, + Root = 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.Root, state_root_d.Root); + } + + [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.Root.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 7e025ad4cd..39eb931901 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; @@ -111,6 +112,7 @@ public void FeeIsMultiSigContract() entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); // Make transaction @@ -180,7 +182,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 @@ -291,7 +293,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 @@ -377,7 +379,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 @@ -464,7 +466,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 @@ -604,7 +606,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 @@ -942,7 +944,7 @@ public void FeeIsSignatureContract_TestScope_Global_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 e63145ff5d..65e5ffa2b9 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 TransactionAttribute[] { }); @@ -389,6 +396,7 @@ public void TestMakeTransaction2() entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; + snapshot.UpdateLocalStateRoot(); snapshot.Commit(); }