From 0a4f7806eb41552c733beb2cfda5c59c12eb095b Mon Sep 17 00:00:00 2001
From: Charis Zhao <eryeer@163.com>
Date: Mon, 23 Sep 2019 21:20:09 +0800
Subject: [PATCH] Unit test For Legder module (#1038)

* update test ledger

* format

* Add enter between classes

* fix ut suggestion

* Update UT_PoolItem.cs

* Update UT_MemoryPool.cs

* Reduce usings

* Reduce changes

* More details on TestReVerifyTopUnverifiedTransactionsIfNeeded and fixed comment on ReVerifyTopUnverifiedTransactionsIfNeeded

* Minor fix on generic transaction generation fee

* Applying dotnet format

* Enhance functions in TestDataCache

* dotnet format

* Cast refactor

* comment tx3 for mempool

* Removing TODO comment
---
 neo.UnitTests/Ledger/UT_Blockchain.cs       | 102 ++++++++
 neo.UnitTests/Ledger/UT_ContractState.cs    |  99 ++++++++
 neo.UnitTests/Ledger/UT_HashIndexState.cs   |  63 +++++
 neo.UnitTests/Ledger/UT_HeaderHashList.cs   |  59 +++++
 neo.UnitTests/Ledger/UT_MemoryPool.cs       | 252 +++++++++++++++++++-
 neo.UnitTests/Ledger/UT_PoolItem.cs         |   1 -
 neo.UnitTests/Ledger/UT_StorageItem.cs      |  10 +
 neo.UnitTests/Ledger/UT_StorageKey.cs       |  26 ++
 neo.UnitTests/Ledger/UT_TransactionState.cs |  67 ++++++
 neo.UnitTests/Ledger/UT_TrimmedBlock.cs     | 138 +++++++++++
 neo.UnitTests/TestBlockchain.cs             |  13 +-
 neo.UnitTests/TestDataCache.cs              |  22 +-
 neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs |  11 +-
 neo/Ledger/MemoryPool.cs                    |   3 +-
 14 files changed, 837 insertions(+), 29 deletions(-)
 create mode 100644 neo.UnitTests/Ledger/UT_Blockchain.cs
 create mode 100644 neo.UnitTests/Ledger/UT_ContractState.cs
 create mode 100644 neo.UnitTests/Ledger/UT_HashIndexState.cs
 create mode 100644 neo.UnitTests/Ledger/UT_HeaderHashList.cs
 create mode 100644 neo.UnitTests/Ledger/UT_TransactionState.cs
 create mode 100644 neo.UnitTests/Ledger/UT_TrimmedBlock.cs

diff --git a/neo.UnitTests/Ledger/UT_Blockchain.cs b/neo.UnitTests/Ledger/UT_Blockchain.cs
new file mode 100644
index 0000000000..eb17e71c62
--- /dev/null
+++ b/neo.UnitTests/Ledger/UT_Blockchain.cs
@@ -0,0 +1,102 @@
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.IO;
+using Neo.Ledger;
+using Neo.Network.P2P.Payloads;
+using Neo.Persistence;
+
+namespace Neo.UnitTests.Ledger
+{
+    internal class TestBlock : Block
+    {
+        public override bool Verify(Snapshot snapshot)
+        {
+            return true;
+        }
+
+        public static TestBlock Cast(Block input)
+        {
+            return input.ToArray().AsSerializable<TestBlock>();
+        }
+    }
+
+    internal class TestHeader : Header
+    {
+        public override bool Verify(Snapshot snapshot)
+        {
+            return true;
+        }
+
+        public static TestHeader Cast(Header input)
+        {
+            return input.ToArray().AsSerializable<TestHeader>();
+        }
+    }
+
+    [TestClass]
+    public class UT_Blockchain
+    {
+        private NeoSystem system;
+        private Store store;
+        Transaction txSample = Blockchain.GenesisBlock.Transactions[0];
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            system = TestBlockchain.InitializeMockNeoSystem();
+            store = TestBlockchain.GetStore();
+            Blockchain.Singleton.MemPool.TryAdd(txSample.Hash, txSample);
+        }
+
+        [TestMethod]
+        public void TestConstructor()
+        {
+            system.ActorSystem.ActorOf(Blockchain.Props(system, store)).Should().NotBeSameAs(system.Blockchain);
+        }
+
+        [TestMethod]
+        public void TestContainsBlock()
+        {
+            Blockchain.Singleton.ContainsBlock(UInt256.Zero).Should().BeFalse();
+        }
+
+        [TestMethod]
+        public void TestContainsTransaction()
+        {
+            Blockchain.Singleton.ContainsTransaction(UInt256.Zero).Should().BeFalse();
+            Blockchain.Singleton.ContainsTransaction(txSample.Hash).Should().BeTrue();
+        }
+
+        [TestMethod]
+        public void TestGetCurrentBlockHash()
+        {
+            Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("5662a113d8fa9532ea9c52046a463e2e3fcfcdd6192d99cad805b376fb643ceb"));
+        }
+
+        [TestMethod]
+        public void TestGetCurrentHeaderHash()
+        {
+            Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("5662a113d8fa9532ea9c52046a463e2e3fcfcdd6192d99cad805b376fb643ceb"));
+        }
+
+        [TestMethod]
+        public void TestGetBlock()
+        {
+            Blockchain.Singleton.GetBlock(UInt256.Zero).Should().BeNull();
+        }
+
+        [TestMethod]
+        public void TestGetBlockHash()
+        {
+            Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("5662a113d8fa9532ea9c52046a463e2e3fcfcdd6192d99cad805b376fb643ceb"));
+            Blockchain.Singleton.GetBlockHash(10).Should().BeNull();
+        }
+
+        [TestMethod]
+        public void TestGetTransaction()
+        {
+            Blockchain.Singleton.GetTransaction(UInt256.Zero).Should().BeNull();
+            Blockchain.Singleton.GetTransaction(txSample.Hash).Should().NotBeNull();
+        }
+    }
+}
diff --git a/neo.UnitTests/Ledger/UT_ContractState.cs b/neo.UnitTests/Ledger/UT_ContractState.cs
new file mode 100644
index 0000000000..87f1d7ac88
--- /dev/null
+++ b/neo.UnitTests/Ledger/UT_ContractState.cs
@@ -0,0 +1,99 @@
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.IO;
+using Neo.IO.Json;
+using Neo.Ledger;
+using Neo.SmartContract;
+using Neo.SmartContract.Manifest;
+using System.IO;
+
+namespace Neo.UnitTests.Ledger
+{
+    [TestClass]
+    public class UT_ContractState
+    {
+        ContractState contract;
+        byte[] script = { 0x01 };
+        ContractManifest manifest;
+
+        [TestInitialize]
+        public void TestSetup()
+        {
+            manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"));
+            contract = new ContractState
+            {
+                Script = script,
+                Manifest = manifest
+            };
+        }
+
+        [TestMethod]
+        public void TestGetHasStorage()
+        {
+            contract.HasStorage.Should().BeFalse();
+        }
+
+        [TestMethod]
+        public void TestGetPayable()
+        {
+            contract.Payable.Should().BeFalse();
+        }
+
+        [TestMethod]
+        public void TestGetScriptHash()
+        {
+            // _scriptHash == null
+            contract.ScriptHash.Should().Be(script.ToScriptHash());
+            // _scriptHash != null
+            contract.ScriptHash.Should().Be(script.ToScriptHash());
+        }
+
+        [TestMethod]
+        public void TestClone()
+        {
+            ICloneable<ContractState> cloneable = contract;
+            ContractState clone = cloneable.Clone();
+            clone.ToJson().ToString().Should().Be(contract.ToJson().ToString());
+        }
+
+        [TestMethod]
+        public void TestFromReplica()
+        {
+            ICloneable<ContractState> cloneable = new ContractState();
+            cloneable.FromReplica(contract);
+            ((ContractState)cloneable).ToJson().ToString().Should().Be(contract.ToJson().ToString());
+        }
+
+        [TestMethod]
+        public void TestDeserialize()
+        {
+            ISerializable newContract = new ContractState();
+            using (MemoryStream ms = new MemoryStream(1024))
+            using (BinaryWriter writer = new BinaryWriter(ms))
+            using (BinaryReader reader = new BinaryReader(ms))
+            {
+                ((ISerializable)contract).Serialize(writer);
+                ms.Seek(0, SeekOrigin.Begin);
+                newContract.Deserialize(reader);
+            }
+            ((ContractState)newContract).Manifest.ToJson().ToString().Should().Be(contract.Manifest.ToJson().ToString());
+            ((ContractState)newContract).Script.Should().BeEquivalentTo(contract.Script);
+        }
+
+        [TestMethod]
+        public void TestGetSize()
+        {
+            ISerializable newContract = contract;
+            newContract.Size.Should().Be(355);
+        }
+
+        [TestMethod]
+        public void TestToJson()
+        {
+            JObject json = contract.ToJson();
+            json["hash"].AsString().Should().Be("0x820944cfdc70976602d71b0091445eedbc661bc5");
+            json["script"].AsString().Should().Be("01");
+            json["manifest"].AsString().Should().Be(manifest.ToJson().AsString());
+        }
+    }
+}
diff --git a/neo.UnitTests/Ledger/UT_HashIndexState.cs b/neo.UnitTests/Ledger/UT_HashIndexState.cs
new file mode 100644
index 0000000000..bfc85479dd
--- /dev/null
+++ b/neo.UnitTests/Ledger/UT_HashIndexState.cs
@@ -0,0 +1,63 @@
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.IO;
+using Neo.Ledger;
+using System.IO;
+
+namespace Neo.UnitTests.Ledger
+{
+    [TestClass]
+    public class UT_HashIndexState
+    {
+        HashIndexState origin;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            origin = new HashIndexState
+            {
+                Hash = UInt256.Zero,
+                Index = 10
+            };
+        }
+
+        [TestMethod]
+        public void TestClone()
+        {
+            HashIndexState dest = ((ICloneable<HashIndexState>)origin).Clone();
+            dest.Hash.Should().Be(origin.Hash);
+            dest.Index.Should().Be(origin.Index);
+        }
+
+        [TestMethod]
+        public void TestFromReplica()
+        {
+            HashIndexState dest = new HashIndexState();
+            ((ICloneable<HashIndexState>)dest).FromReplica(origin);
+            dest.Hash.Should().Be(origin.Hash);
+            dest.Index.Should().Be(origin.Index);
+        }
+
+        [TestMethod]
+        public void TestGetSize()
+        {
+            ((ISerializable)origin).Size.Should().Be(36);
+        }
+
+        [TestMethod]
+        public void TestDeserialize()
+        {
+            using (MemoryStream ms = new MemoryStream(1024))
+            using (BinaryWriter writer = new BinaryWriter(ms))
+            using (BinaryReader reader = new BinaryReader(ms))
+            {
+                ((ISerializable)origin).Serialize(writer);
+                ms.Seek(0, SeekOrigin.Begin);
+                HashIndexState dest = new HashIndexState();
+                ((ISerializable)dest).Deserialize(reader);
+                dest.Hash.Should().Be(origin.Hash);
+                dest.Index.Should().Be(origin.Index);
+            }
+        }
+    }
+}
diff --git a/neo.UnitTests/Ledger/UT_HeaderHashList.cs b/neo.UnitTests/Ledger/UT_HeaderHashList.cs
new file mode 100644
index 0000000000..2f198f4c6c
--- /dev/null
+++ b/neo.UnitTests/Ledger/UT_HeaderHashList.cs
@@ -0,0 +1,59 @@
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.IO;
+using Neo.Ledger;
+using System.IO;
+
+namespace Neo.UnitTests.Ledger
+{
+    [TestClass]
+    public class UT_HeaderHashList
+    {
+        HeaderHashList origin;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            origin = new HeaderHashList
+            {
+                Hashes = new UInt256[] { UInt256.Zero }
+            };
+        }
+
+        [TestMethod]
+        public void TestClone()
+        {
+            HeaderHashList dest = ((ICloneable<HeaderHashList>)origin).Clone();
+            dest.Hashes.Should().BeEquivalentTo(origin.Hashes);
+        }
+
+        [TestMethod]
+        public void TestFromReplica()
+        {
+            HeaderHashList dest = new HeaderHashList();
+            ((ICloneable<HeaderHashList>)dest).FromReplica(origin);
+            dest.Hashes.Should().BeEquivalentTo(origin.Hashes);
+        }
+
+        [TestMethod]
+        public void TestDeserialize()
+        {
+            using (MemoryStream ms = new MemoryStream(1024))
+            using (BinaryWriter writer = new BinaryWriter(ms))
+            using (BinaryReader reader = new BinaryReader(ms))
+            {
+                ((ISerializable)origin).Serialize(writer);
+                ms.Seek(0, SeekOrigin.Begin);
+                HeaderHashList dest = new HeaderHashList();
+                ((ISerializable)dest).Deserialize(reader);
+                dest.Hashes.Should().BeEquivalentTo(origin.Hashes);
+            }
+        }
+
+        [TestMethod]
+        public void TestGetSize()
+        {
+            ((ISerializable)origin).Size.Should().Be(33);
+        }
+    }
+}
diff --git a/neo.UnitTests/Ledger/UT_MemoryPool.cs b/neo.UnitTests/Ledger/UT_MemoryPool.cs
index ea797d7c03..e9a451952c 100644
--- a/neo.UnitTests/Ledger/UT_MemoryPool.cs
+++ b/neo.UnitTests/Ledger/UT_MemoryPool.cs
@@ -1,19 +1,36 @@
 using FluentAssertions;
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Moq;
+using Neo.Cryptography;
+using Neo.IO;
+using Neo.IO.Caching;
 using Neo.Ledger;
 using Neo.Network.P2P.Payloads;
 using Neo.Persistence;
+using Neo.Plugins;
+using Neo.SmartContract.Native;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 
 namespace Neo.UnitTests.Ledger
 {
+    internal class TestIMemoryPoolTxObserverPlugin : Plugin, IMemoryPoolTxObserverPlugin
+    {
+        public override void Configure() { }
+        public void TransactionAdded(Transaction tx) { }
+        public void TransactionsRemoved(MemoryPoolTxRemovalReason reason, IEnumerable<Transaction> transactions) { }
+    }
+
     [TestClass]
     public class UT_MemoryPool
     {
+        private const byte Prefix_MaxTransactionsPerBlock = 23;
+        private const byte Prefix_FeePerByte = 10;
         private MemoryPool _unit;
+        private MemoryPool _unit2;
+        private TestIMemoryPoolTxObserverPlugin plugin;
 
         [TestInitialize]
         public void TestSetup()
@@ -33,6 +50,14 @@ public void TestSetup()
             _unit.VerifiedCount.ShouldBeEquivalentTo(0);
             _unit.UnVerifiedCount.ShouldBeEquivalentTo(0);
             _unit.Count.ShouldBeEquivalentTo(0);
+            _unit2 = new MemoryPool(TheNeoSystem, 0);
+            plugin = new TestIMemoryPoolTxObserverPlugin();
+        }
+
+        [TestCleanup]
+        public void CleanUp()
+        {
+            Plugin.TxObserverPlugins.Remove(plugin);
         }
 
         long LongRandom(long min, long max, Random rand)
@@ -66,8 +91,10 @@ private Transaction CreateTransactionWithFee(long fee)
             return mock.Object;
         }
 
-        private Transaction CreateTransaction()
+        private Transaction CreateTransaction(long fee = -1)
         {
+            if (fee != -1)
+                return CreateTransactionWithFee(fee);
             return CreateTransactionWithFee(LongRandom(100000, 100000000, TestUtils.TestRandom));
         }
 
@@ -82,6 +109,10 @@ private void AddTransactions(int count)
             Console.WriteLine($"created {count} tx");
         }
 
+        private void AddTransaction(Transaction txToAdd)
+        {
+            _unit.TryAdd(txToAdd.Hash, txToAdd);
+        }
 
         [TestMethod]
         public void CapacityTest()
@@ -139,7 +170,7 @@ public void BlockPersistMovesTxToUnverifiedAndReverification()
             _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0);
         }
 
-        private void verifyTransactionsSortedDescending(IEnumerable<Transaction> transactions)
+        private void VerifyTransactionsSortedDescending(IEnumerable<Transaction> transactions)
         {
             Transaction lastTransaction = null;
             foreach (var tx in transactions)
@@ -170,7 +201,7 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst()
             var sortedVerifiedTxs = _unit.GetSortedVerifiedTransactions().ToList();
             // verify all 100 transactions are returned in sorted order
             sortedVerifiedTxs.Count.ShouldBeEquivalentTo(100);
-            verifyTransactionsSortedDescending(sortedVerifiedTxs);
+            VerifyTransactionsSortedDescending(sortedVerifiedTxs);
 
             // move all to unverified
             var block = new Block { Transactions = new Transaction[0] };
@@ -185,7 +216,7 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst()
                 _unit.GetVerifiedAndUnverifiedTransactions(out var sortedVerifiedTransactions, out var sortedUnverifiedTransactions);
                 sortedVerifiedTransactions.Count().ShouldBeEquivalentTo(0);
                 var sortedUnverifiedArray = sortedUnverifiedTransactions.ToArray();
-                verifyTransactionsSortedDescending(sortedUnverifiedArray);
+                VerifyTransactionsSortedDescending(sortedUnverifiedArray);
                 var maxTransaction = sortedUnverifiedArray.First();
                 var minTransaction = sortedUnverifiedArray.Last();
 
@@ -253,5 +284,218 @@ public void TestInvalidateAll()
             _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(30);
             _unit.SortedTxCount.ShouldBeEquivalentTo(0);
         }
+
+        [TestMethod]
+        public void TestContainsKey()
+        {
+            AddTransactions(10);
+
+            var txToAdd = CreateTransaction();
+            _unit.TryAdd(txToAdd.Hash, txToAdd);
+            _unit.ContainsKey(txToAdd.Hash).Should().BeTrue();
+            _unit.InvalidateVerifiedTransactions();
+            _unit.ContainsKey(txToAdd.Hash).Should().BeTrue();
+        }
+
+        [TestMethod]
+        public void TestGetEnumerator()
+        {
+            AddTransactions(10);
+            _unit.InvalidateVerifiedTransactions();
+            IEnumerator<Transaction> enumerator = _unit.GetEnumerator();
+            foreach (Transaction tx in _unit)
+            {
+                enumerator.MoveNext();
+                enumerator.Current.Should().BeSameAs(tx);
+            }
+        }
+
+        [TestMethod]
+        public void TestIEnumerableGetEnumerator()
+        {
+            AddTransactions(10);
+            _unit.InvalidateVerifiedTransactions();
+            IEnumerable enumerable = _unit;
+            var enumerator = enumerable.GetEnumerator();
+            foreach (Transaction tx in _unit)
+            {
+                enumerator.MoveNext();
+                enumerator.Current.Should().BeSameAs(tx);
+            }
+        }
+
+        [TestMethod]
+        public void TestGetVerifiedTransactions()
+        {
+            var tx1 = CreateTransaction();
+            var tx2 = CreateTransaction();
+            _unit.TryAdd(tx1.Hash, tx1);
+            _unit.InvalidateVerifiedTransactions();
+            _unit.TryAdd(tx2.Hash, tx2);
+            IEnumerable<Transaction> enumerable = _unit.GetVerifiedTransactions();
+            enumerable.Count().Should().Be(1);
+            var enumerator = enumerable.GetEnumerator();
+            enumerator.MoveNext();
+            enumerator.Current.Should().BeSameAs(tx2);
+        }
+
+        [TestMethod]
+        public void TestReVerifyTopUnverifiedTransactionsIfNeeded()
+        {
+            NeoSystem TheNeoSystem = TestBlockchain.InitializeMockNeoSystem();
+            var s = Blockchain.Singleton.Height;
+            _unit = new MemoryPool(TheNeoSystem, 600);
+            _unit.LoadPolicy(TestBlockchain.GetStore().GetSnapshot());
+            AddTransaction(CreateTransaction(100000001));
+            AddTransaction(CreateTransaction(100000001));
+            AddTransaction(CreateTransaction(100000001));
+            AddTransaction(CreateTransaction(1));
+            _unit.VerifiedCount.Should().Be(4);
+            _unit.UnVerifiedCount.Should().Be(0);
+
+            _unit.InvalidateVerifiedTransactions();
+            _unit.VerifiedCount.Should().Be(0);
+            _unit.UnVerifiedCount.Should().Be(4);
+
+            AddTransactions(511); // Max per block currently is 512
+            _unit.VerifiedCount.Should().Be(511);
+            _unit.UnVerifiedCount.Should().Be(4);
+
+            var result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(1, Blockchain.Singleton.GetSnapshot());
+            result.Should().BeTrue();
+            _unit.VerifiedCount.Should().Be(512);
+            _unit.UnVerifiedCount.Should().Be(3);
+
+            result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(2, Blockchain.Singleton.GetSnapshot());
+            result.Should().BeTrue();
+            _unit.VerifiedCount.Should().Be(514);
+            _unit.UnVerifiedCount.Should().Be(1);
+
+            result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(3, Blockchain.Singleton.GetSnapshot());
+            result.Should().BeFalse();
+            _unit.VerifiedCount.Should().Be(515);
+            _unit.UnVerifiedCount.Should().Be(0);
+        }
+
+        [TestMethod]
+        public void TestTryAdd()
+        {
+            var tx1 = CreateTransaction();
+            _unit.TryAdd(tx1.Hash, tx1).Should().BeTrue();
+            _unit.TryAdd(tx1.Hash, tx1).Should().BeFalse();
+            _unit2.TryAdd(tx1.Hash, tx1).Should().BeFalse();
+        }
+
+        [TestMethod]
+        public void TestTryGetValue()
+        {
+            var tx1 = CreateTransaction();
+            _unit.TryAdd(tx1.Hash, tx1);
+            _unit.TryGetValue(tx1.Hash, out Transaction tx).Should().BeTrue();
+            tx.ShouldBeEquivalentTo(tx1);
+
+            _unit.InvalidateVerifiedTransactions();
+            _unit.TryGetValue(tx1.Hash, out tx).Should().BeTrue();
+            tx.ShouldBeEquivalentTo(tx1);
+
+            var tx2 = CreateTransaction();
+            _unit.TryGetValue(tx2.Hash, out tx).Should().BeFalse();
+        }
+
+        [TestMethod]
+        public void TestUpdatePoolForBlockPersisted()
+        {
+            var mockSnapshot = new Mock<Snapshot>();
+            byte[] transactionsPerBlock = { 0x18, 0x00, 0x00, 0x00 }; // 24
+            byte[] feePerByte = { 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 1048576
+            StorageItem item1 = new StorageItem
+            {
+                Value = transactionsPerBlock
+            };
+            StorageItem item2 = new StorageItem
+            {
+                Value = feePerByte
+            };
+            var myDataCache = new MyDataCache<StorageKey, StorageItem>();
+            var key1 = CreateStorageKey(Prefix_MaxTransactionsPerBlock);
+            var key2 = CreateStorageKey(Prefix_FeePerByte);
+            key1.ScriptHash = NativeContract.Policy.Hash;
+            key2.ScriptHash = NativeContract.Policy.Hash;
+            myDataCache.Add(key1, item1);
+            myDataCache.Add(key2, item2);
+            mockSnapshot.SetupGet(p => p.Storages).Returns(myDataCache);
+
+            var tx1 = CreateTransaction();
+            var tx2 = CreateTransaction();
+            Transaction[] transactions = { tx1, tx2 };
+            _unit.TryAdd(tx1.Hash, tx1);
+
+            var block = new Block { Transactions = transactions };
+
+            _unit.UnVerifiedCount.Should().Be(0);
+            _unit.VerifiedCount.Should().Be(1);
+
+            _unit.UpdatePoolForBlockPersisted(block, mockSnapshot.Object);
+
+            _unit.UnVerifiedCount.Should().Be(0);
+            _unit.VerifiedCount.Should().Be(0);
+        }
+
+        public StorageKey CreateStorageKey(byte prefix, byte[] key = null)
+        {
+            StorageKey storageKey = new StorageKey
+            {
+                ScriptHash = null,
+                Key = new byte[sizeof(byte) + (key?.Length ?? 0)]
+            };
+            storageKey.Key[0] = prefix;
+            if (key != null)
+                Buffer.BlockCopy(key, 0, storageKey.Key, 1, key.Length);
+            return storageKey;
+        }
+    }
+
+    public class MyDataCache<TKey, TValue> : DataCache<TKey, TValue>
+        where TKey : IEquatable<TKey>, ISerializable
+        where TValue : class, ICloneable<TValue>, ISerializable, new()
+    {
+        private readonly TValue _defaultValue;
+
+        public MyDataCache()
+        {
+            _defaultValue = null;
+        }
+
+        public MyDataCache(TValue defaultValue)
+        {
+            this._defaultValue = defaultValue;
+        }
+        public override void DeleteInternal(TKey key)
+        {
+        }
+
+        protected override void AddInternal(TKey key, TValue value)
+        {
+            Add(key, value);
+        }
+
+        protected override IEnumerable<KeyValuePair<TKey, TValue>> FindInternal(byte[] key_prefix)
+        {
+            return Enumerable.Empty<KeyValuePair<TKey, TValue>>();
+        }
+
+        protected override TValue GetInternal(TKey key)
+        {
+            return TryGet(key);
+        }
+
+        protected override TValue TryGetInternal(TKey key)
+        {
+            return _defaultValue;
+        }
+
+        protected override void UpdateInternal(TKey key, TValue value)
+        {
+        }
     }
 }
diff --git a/neo.UnitTests/Ledger/UT_PoolItem.cs b/neo.UnitTests/Ledger/UT_PoolItem.cs
index 10a57ecc90..d94cec8e4c 100644
--- a/neo.UnitTests/Ledger/UT_PoolItem.cs
+++ b/neo.UnitTests/Ledger/UT_PoolItem.cs
@@ -11,7 +11,6 @@ namespace Neo.UnitTests.Ledger
     [TestClass]
     public class UT_PoolItem
     {
-        //private PoolItem uut;
         private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism
 
         [TestInitialize]
diff --git a/neo.UnitTests/Ledger/UT_StorageItem.cs b/neo.UnitTests/Ledger/UT_StorageItem.cs
index 9afab26372..5ed24b5858 100644
--- a/neo.UnitTests/Ledger/UT_StorageItem.cs
+++ b/neo.UnitTests/Ledger/UT_StorageItem.cs
@@ -106,5 +106,15 @@ public void Serialize()
             }
         }
 
+        [TestMethod]
+        public void TestFromReplica()
+        {
+            uut.Value = TestUtils.GetByteArray(10, 0x42);
+            uut.IsConstant = true;
+            StorageItem dest = new StorageItem();
+            ((ICloneable<StorageItem>)dest).FromReplica(uut);
+            dest.Value.Should().BeEquivalentTo(uut.Value);
+            dest.IsConstant.Should().Be(uut.IsConstant);
+        }
     }
 }
diff --git a/neo.UnitTests/Ledger/UT_StorageKey.cs b/neo.UnitTests/Ledger/UT_StorageKey.cs
index 6e2223cdcb..1941cbc0cc 100644
--- a/neo.UnitTests/Ledger/UT_StorageKey.cs
+++ b/neo.UnitTests/Ledger/UT_StorageKey.cs
@@ -2,6 +2,7 @@
 using Microsoft.VisualStudio.TestTools.UnitTesting;
 using Neo.IO;
 using Neo.Ledger;
+using System.IO;
 
 namespace Neo.UnitTests.Ledger
 {
@@ -127,5 +128,30 @@ public void GetHashCode_Get()
             uut.Key = TestUtils.GetByteArray(10, 0x42);
             uut.GetHashCode().Should().Be(806209853);
         }
+
+        [TestMethod]
+        public void Equals_Obj()
+        {
+            uut.Equals(1u).Should().BeFalse();
+            uut.Equals((object)uut).Should().BeTrue();
+        }
+
+        [TestMethod]
+        public void TestDeserialize()
+        {
+            using (MemoryStream ms = new MemoryStream(1024))
+            using (BinaryWriter writer = new BinaryWriter(ms))
+            using (BinaryReader reader = new BinaryReader(ms))
+            {
+                uut.ScriptHash = new UInt160(TestUtils.GetByteArray(20, 0x42));
+                uut.Key = TestUtils.GetByteArray(10, 0x42);
+                ((ISerializable)uut).Serialize(writer);
+                ms.Seek(0, SeekOrigin.Begin);
+                StorageKey dest = new StorageKey();
+                ((ISerializable)dest).Deserialize(reader);
+                dest.ScriptHash.Should().Be(uut.ScriptHash);
+                dest.Key.Should().BeEquivalentTo(uut.Key);
+            }
+        }
     }
 }
diff --git a/neo.UnitTests/Ledger/UT_TransactionState.cs b/neo.UnitTests/Ledger/UT_TransactionState.cs
new file mode 100644
index 0000000000..7abf431849
--- /dev/null
+++ b/neo.UnitTests/Ledger/UT_TransactionState.cs
@@ -0,0 +1,67 @@
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.IO;
+using Neo.Ledger;
+using System.IO;
+
+namespace Neo.UnitTests.Ledger
+{
+    [TestClass]
+    public class UT_TransactionState
+    {
+        TransactionState origin;
+
+        [TestInitialize]
+        public void Initialize()
+        {
+            origin = new TransactionState
+            {
+                BlockIndex = 1,
+                VMState = VM.VMState.NONE,
+                Transaction = Blockchain.GenesisBlock.Transactions[0]
+            };
+        }
+
+        [TestMethod]
+        public void TestClone()
+        {
+            TransactionState dest = ((ICloneable<TransactionState>)origin).Clone();
+            dest.BlockIndex.Should().Be(origin.BlockIndex);
+            dest.VMState.Should().Be(origin.VMState);
+            dest.Transaction.Should().Be(origin.Transaction);
+        }
+
+        [TestMethod]
+        public void TestFromReplica()
+        {
+            TransactionState dest = new TransactionState();
+            ((ICloneable<TransactionState>)dest).FromReplica(origin);
+            dest.BlockIndex.Should().Be(origin.BlockIndex);
+            dest.VMState.Should().Be(origin.VMState);
+            dest.Transaction.Should().Be(origin.Transaction);
+        }
+
+        [TestMethod]
+        public void TestDeserialize()
+        {
+            using (MemoryStream ms = new MemoryStream(1024))
+            using (BinaryWriter writer = new BinaryWriter(ms))
+            using (BinaryReader reader = new BinaryReader(ms))
+            {
+                ((ISerializable)origin).Serialize(writer);
+                ms.Seek(0, SeekOrigin.Begin);
+                TransactionState dest = new TransactionState();
+                ((ISerializable)dest).Deserialize(reader);
+                dest.BlockIndex.Should().Be(origin.BlockIndex);
+                dest.VMState.Should().Be(origin.VMState);
+                dest.Transaction.Should().Be(origin.Transaction);
+            }
+        }
+
+        [TestMethod]
+        public void TestGetSize()
+        {
+            ((ISerializable)origin).Size.Should().Be(62);
+        }
+    }
+}
diff --git a/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/neo.UnitTests/Ledger/UT_TrimmedBlock.cs
new file mode 100644
index 0000000000..3704279701
--- /dev/null
+++ b/neo.UnitTests/Ledger/UT_TrimmedBlock.cs
@@ -0,0 +1,138 @@
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.IO;
+using Neo.Ledger;
+using Neo.Network.P2P.Payloads;
+using Neo.VM;
+using System;
+using System.IO;
+
+namespace Neo.UnitTests.Ledger
+{
+    [TestClass]
+    public class UT_TrimmedBlock
+    {
+        public static TrimmedBlock GetTrimmedBlockWithNoTransaction()
+        {
+            return new TrimmedBlock
+            {
+                ConsensusData = new ConsensusData(),
+                MerkleRoot = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02"),
+                PrevHash = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"),
+                Timestamp = new DateTime(1988, 06, 01, 0, 0, 0, DateTimeKind.Utc).ToTimestamp(),
+                Index = 1,
+                NextConsensus = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"),
+                Witness = new Witness
+                {
+                    InvocationScript = new byte[0],
+                    VerificationScript = new[] { (byte)OpCode.PUSHT }
+                },
+                Hashes = new UInt256[0]
+            };
+        }
+
+        [TestMethod]
+        public void TestGetIsBlock()
+        {
+            TrimmedBlock block = GetTrimmedBlockWithNoTransaction();
+            block.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash };
+            block.IsBlock.Should().BeTrue();
+        }
+
+        [TestMethod]
+        public void TestGetBlock()
+        {
+            var cache = new TestDataCache<UInt256, TransactionState>();
+            var tx1 = TestUtils.GetTransaction();
+            tx1.Script = new byte[] { 0x01,0x01,0x01,0x01,
+                                      0x01,0x01,0x01,0x01,
+                                      0x01,0x01,0x01,0x01,
+                                      0x01,0x01,0x01,0x01 };
+            var state1 = new TransactionState
+            {
+                Transaction = tx1,
+                BlockIndex = 1
+            };
+            var tx2 = TestUtils.GetTransaction();
+            tx2.Script = new byte[] { 0x01,0x01,0x01,0x01,
+                                      0x01,0x01,0x01,0x01,
+                                      0x01,0x01,0x01,0x01,
+                                      0x01,0x01,0x01,0x02 };
+            var state2 = new TransactionState
+            {
+                Transaction = tx2,
+                BlockIndex = 1
+            };
+            cache.Add(tx1.Hash, state1);
+            cache.Add(tx2.Hash, state2);
+
+            TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction();
+            tblock.Hashes = new UInt256[] { tx1.Hash, tx2.Hash };
+            Block block = tblock.GetBlock(cache);
+
+            block.Index.Should().Be(1);
+            block.MerkleRoot.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02"));
+            block.Transactions.Length.Should().Be(1);
+            block.Transactions[0].Hash.Should().Be(tx2.Hash);
+        }
+
+        [TestMethod]
+        public void TestGetHeader()
+        {
+            TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction();
+            Header header = tblock.Header;
+            header.PrevHash.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"));
+            header.MerkleRoot.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02"));
+        }
+
+        [TestMethod]
+        public void TestGetSize()
+        {
+            TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction();
+            tblock.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash };
+            tblock.Size.Should().Be(146);
+        }
+
+        [TestMethod]
+        public void TestDeserialize()
+        {
+            TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction();
+            tblock.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash };
+            var newBlock = new TrimmedBlock();
+            using (MemoryStream ms = new MemoryStream(1024))
+            using (BinaryWriter writer = new BinaryWriter(ms))
+            using (BinaryReader reader = new BinaryReader(ms))
+            {
+                tblock.Serialize(writer);
+                ms.Seek(0, SeekOrigin.Begin);
+                newBlock.Deserialize(reader);
+            }
+            tblock.MerkleRoot.Should().Be(newBlock.MerkleRoot);
+            tblock.PrevHash.Should().Be(newBlock.PrevHash);
+            tblock.Timestamp.Should().Be(newBlock.Timestamp);
+            tblock.Hashes.Length.Should().Be(newBlock.Hashes.Length);
+            tblock.Witness.ScriptHash.Should().Be(newBlock.Witness.ScriptHash);
+            tblock.ToJson().ToString().Should().Be(newBlock.ToJson().ToString());
+        }
+
+        [TestMethod]
+        public void TestClone()
+        {
+            TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction();
+            tblock.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash };
+            ICloneable<TrimmedBlock> cloneable = tblock;
+            var clonedBlock = cloneable.Clone();
+            clonedBlock.ToJson().ToString().Should().Be(tblock.ToJson().ToString());
+        }
+
+        [TestMethod]
+        public void TestFromReplica()
+        {
+            TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction();
+            tblock.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash };
+            ICloneable<TrimmedBlock> cloneable = new TrimmedBlock();
+            cloneable.FromReplica(tblock);
+            ((TrimmedBlock)cloneable).ToJson().ToString().Should().Be(tblock.ToJson().ToString());
+        }
+    }
+}
diff --git a/neo.UnitTests/TestBlockchain.cs b/neo.UnitTests/TestBlockchain.cs
index 5276dc2e4c..290e054d10 100644
--- a/neo.UnitTests/TestBlockchain.cs
+++ b/neo.UnitTests/TestBlockchain.cs
@@ -33,14 +33,13 @@ public static NeoSystem InitializeMockNeoSystem()
                 _Store = new Mock<Store>();
 
                 var defaultTx = TestUtils.CreateRandomHashTransaction();
+                var txState = new TransactionState
+                {
+                    BlockIndex = 1,
+                    Transaction = defaultTx
+                };
                 _Store.Setup(p => p.GetBlocks()).Returns(new TestDataCache<UInt256, TrimmedBlock>());
-                _Store.Setup(p => p.GetTransactions()).Returns(new TestDataCache<UInt256, TransactionState>(
-                    new TransactionState
-                    {
-                        BlockIndex = 1,
-                        Transaction = defaultTx
-                    }));
-
+                _Store.Setup(p => p.GetTransactions()).Returns(new TestDataCache<UInt256, TransactionState>(defaultTx.Hash, txState));
                 _Store.Setup(p => p.GetContracts()).Returns(new TestDataCache<UInt160, ContractState>());
                 _Store.Setup(p => p.GetStorages()).Returns(new TestDataCache<StorageKey, StorageItem>());
                 _Store.Setup(p => p.GetHeaderHashList()).Returns(new TestDataCache<UInt32Wrapper, HeaderHashList>());
diff --git a/neo.UnitTests/TestDataCache.cs b/neo.UnitTests/TestDataCache.cs
index 44d86141b5..b468e09e4c 100644
--- a/neo.UnitTests/TestDataCache.cs
+++ b/neo.UnitTests/TestDataCache.cs
@@ -10,43 +10,43 @@ public class TestDataCache<TKey, TValue> : DataCache<TKey, TValue>
         where TKey : IEquatable<TKey>, ISerializable
         where TValue : class, ICloneable<TValue>, ISerializable, new()
     {
-        private readonly TValue _defaultValue;
+        private readonly Dictionary<TKey, TValue> dic = new Dictionary<TKey, TValue>();
 
-        public TestDataCache()
-        {
-            _defaultValue = null;
-        }
+        public TestDataCache() { }
 
-        public TestDataCache(TValue defaultValue)
+        public TestDataCache(TKey key, TValue value)
         {
-            this._defaultValue = defaultValue;
+            dic.Add(key, value);
         }
         public override void DeleteInternal(TKey key)
         {
+            dic.Remove(key);
         }
 
         protected override void AddInternal(TKey key, TValue value)
         {
+            dic.Add(key, value);
         }
 
         protected override IEnumerable<KeyValuePair<TKey, TValue>> FindInternal(byte[] key_prefix)
         {
-            return Enumerable.Empty<KeyValuePair<TKey, TValue>>();
+            return dic.ToList();
         }
 
         protected override TValue GetInternal(TKey key)
         {
-            if (_defaultValue == null) throw new NotImplementedException();
-            return _defaultValue;
+            if (dic[key] == null) throw new NotImplementedException();
+            return dic[key];
         }
 
         protected override TValue TryGetInternal(TKey key)
         {
-            return _defaultValue;
+            return dic.TryGetValue(key, out TValue value) ? value : null;
         }
 
         protected override void UpdateInternal(TKey key, TValue value)
         {
+            dic[key] = value;
         }
     }
 }
diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs
index 294afd91cd..6a8c16dbb6 100644
--- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs
+++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs
@@ -17,10 +17,11 @@ namespace Neo.UnitTests.Wallets.NEP6
     public class UT_NEP6Wallet
     {
         private NEP6Wallet uut;
-        private static string wPath;
+        private string wPath;
         private static KeyPair keyPair;
         private static string nep2key;
         private static UInt160 testScriptHash;
+        private string rootPath;
 
         public static string GetRandomPath()
         {
@@ -48,9 +49,9 @@ private NEP6Wallet CreateWallet()
 
         private string CreateWalletFile()
         {
-            string path = GetRandomPath();
-            if (!Directory.Exists(path)) Directory.CreateDirectory(path);
-            path = Path.Combine(path, "wallet.json");
+            rootPath = GetRandomPath();
+            if (!Directory.Exists(rootPath)) Directory.CreateDirectory(rootPath);
+            string path = Path.Combine(rootPath, "wallet.json");
             File.WriteAllText(path, "{\"name\":\"name\",\"version\":\"0.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":{}}");
             return path;
         }
@@ -66,6 +67,7 @@ public void TestSetup()
         public void TestCleanUp()
         {
             if (File.Exists(wPath)) File.Delete(wPath);
+            if (Directory.Exists(rootPath)) Directory.Delete(rootPath);
         }
 
         [TestMethod]
@@ -328,6 +330,7 @@ public void TestMigrate()
             NEP6Wallet nw = NEP6Wallet.Migrate(npath, path, "123");
             bool result = nw.Contains(testScriptHash);
             Assert.AreEqual(true, result);
+            if (File.Exists(path)) File.Delete(path);
         }
 
         [TestMethod]
diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs
index a6af9fc721..10ee5f9d0b 100644
--- a/neo/Ledger/MemoryPool.cs
+++ b/neo/Ledger/MemoryPool.cs
@@ -469,8 +469,7 @@ private int ReverifyTransactions(SortedSet<PoolItem> verifiedSortedTxPool,
         ///
         /// Note: this must only be called from a single thread (the Blockchain actor)
         /// </summary>
-        /// <param name="maxToVerify">Max transactions to reverify, the value passed should be >=2. If 1 is passed it
-        ///                           will still potentially use 2.</param>
+        /// <param name="maxToVerify">Max transactions to reverify, the value passed cam be >=1</param>
         /// <param name="snapshot">The snapshot to use for verifying.</param>
         /// <returns>true if more unsorted messages exist, otherwise false</returns>
         internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, Snapshot snapshot)