From 493f10bcbcb007662663be38970a485f7d3e07d1 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 10 Jan 2020 17:52:54 +0800 Subject: [PATCH 001/171] first push --- src/neo/Trie/Database.cs | 8 ++ src/neo/Trie/MPT/Helper.cs | 59 ++++++++ src/neo/Trie/MPT/MPTDatabase.cs | 29 ++++ src/neo/Trie/MPT/MPTNode.cs | 155 +++++++++++++++++++++ src/neo/Trie/MPT/MPTTrie.cs | 104 ++++++++++++++ src/neo/Trie/Trie.cs | 8 ++ tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 26 ++++ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 56 ++++++++ tests/neo.UnitTests/Trie/MPT/UT_Node.cs | 0 9 files changed, 445 insertions(+) create mode 100644 src/neo/Trie/Database.cs create mode 100644 src/neo/Trie/MPT/Helper.cs create mode 100644 src/neo/Trie/MPT/MPTDatabase.cs create mode 100644 src/neo/Trie/MPT/MPTNode.cs create mode 100644 src/neo/Trie/MPT/MPTTrie.cs create mode 100644 src/neo/Trie/Trie.cs create mode 100644 tests/neo.UnitTests/Trie/MPT/UT_Helper.cs create mode 100644 tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs create mode 100644 tests/neo.UnitTests/Trie/MPT/UT_Node.cs diff --git a/src/neo/Trie/Database.cs b/src/neo/Trie/Database.cs new file mode 100644 index 0000000000..ff16c9e0de --- /dev/null +++ b/src/neo/Trie/Database.cs @@ -0,0 +1,8 @@ + +namespace Neo.Trie +{ + public abstract class Database + { + + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs new file mode 100644 index 0000000000..bf15117ef6 --- /dev/null +++ b/src/neo/Trie/MPT/Helper.cs @@ -0,0 +1,59 @@ +using System; + +namespace Neo.Trie.MPT +{ + public static class Helper + { + public static byte[] Concat(this byte[] a, byte[] b) + { + var result = new byte[a.Length + b.Length]; + a.CopyTo(result, 0); + b.CopyTo(result, a.Length); + return result; + } + + public static byte[] CommonPrefix(this byte[] a, byte[] b) + { + var prefix = new byte[]{}; + var minLen = a.Length <= b.Length ? a.Length : b.Length; + + if (a.Length == 0 || b.Length == 0 || a[0] != b[0]) return prefix; + + for (int i = 0; i < minLen; i++) + { + if (a[i] != b[i]) break; + prefix = prefix.Add(a[i]); + } + return prefix; + } + + public static bool Equal(this byte[] a, byte[] b) + { + if (a.Length != b.Length) return false; + for (int i = 0; i < a.Length; i++) + { + if (a[i] != b[i]) return false; + } + return true; + } + + public static byte[] Skip(this byte[] a, int count) + { + var result = new byte[]{}; + var len = a.Length - count; + if (0 < len) { + result = new byte[len]; + Array.Copy(a, count, result, 0, len); + } + return result; + } + + public static byte[] Add(this byte[] a, byte b) + { + var result = new byte[a.Length + 1]; + a.CopyTo(result, 0); + result[a.Length] = b; + return result; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs new file mode 100644 index 0000000000..8aa5004f25 --- /dev/null +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -0,0 +1,29 @@ + +using Neo.Persistence; + +namespace Neo.Trie.MPT +{ + public class MPTDatabase: Database + { + private IStore store; + + private byte TABLE = 0x54; + + private byte[] GetStoreKey(byte[] hash) + { + return hash; + } + + public MPTDatabase(IStore store) + { + this.store = store; + } + + public MPTNode Node(byte[] hash) + { + var data = store.TryGet(TABLE, GetStoreKey(hash)); + var n = MPTNode.Decode(data); + return n; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs new file mode 100644 index 0000000000..34a35fa48e --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -0,0 +1,155 @@ +using System.IO; +using Neo.IO; +using Neo.Cryptography; + +namespace Neo.Trie.MPT +{ + enum NodeType + { + BranchNode, + ExtensionNode, + LeafNode, + HashNode, + ValueNode + } + public class NodeFlag + { + public byte[] Hash; + public bool dirty; + } + public abstract class MPTNode: ISerializable + { + + public NodeFlag Flag { get; } + + public int Size { get; } + + public static MPTNode Decode(byte[] data) + { + var nodeType = (NodeType)data[0]; + data = data.Skip(1); + switch (nodeType) + { + case NodeType.BranchNode: + return BranchNode.Decode(data); + case NodeType.ExtensionNode: + return ExtensionNode.Decode(data); + case NodeType.LeafNode: + return LeafNode.Decode(data); + case NodeType.ValueNode: + return ValueNode.Decode(data); + default: + throw new System.Exception(); + } + } + + } + + public class ExtensionNode : MPTNode + { + public byte[] Key; + public MPTNode Next; + + public ExtensionNode Clone() + { + var cloned = new ExtensionNode(); + cloned.Key = (byte[])Key.Clone(); + cloned.Next = Next; + return cloned; + } + + public byte[] Encode() + { + return new byte[]{}; + } + + public new static ExtensionNode Decode(byte[] data) + { + var n = new ExtensionNode(); + return n; + } + } + + public class BranchNode : MPTNode + { + public MPTNode[] Children = new MPTNode[17]; + + public BranchNode Clone() + { + var cloned = new BranchNode(); + for (int i = 0; i < Children.Length; i++) + { + cloned.Children[i] = Children[i]; + } + return cloned; + } + + public override void Serialize(BinaryWriter writer) + { + writer.WriteNullableArray(Children); + } + + public override void Deserialize(BinaryReader reader) + { + + } + } + + public class LeafNode : MPTNode + { + public byte[] Key; + public MPTNode Value; + + public override void Serialize(BinaryWriter writer) + { + + } + + public override void Deserialize(BinaryReader reader) + { + Value = new HashNode(reader.ReadVarBytes()); + } + } + + public class HashNode : MPTNode + { + public byte[] Hash; + + public HashNode(byte[] hash) + { + Hash = new byte[hash.Length]; + hash.CopyTo(Hash, 0); + } + + public override void Serialize(BinaryWriter writer) + { + writer.WriteVarBytes(Hash); + } + + public override void Deserialize(BinaryReader reader) + { + Hash = reader.ReadVarBytes(); + } + } + + public class ValueNode : MPTNode + { + public byte[] Value; + + public ValueNode(byte[] val) + { + Value = new byte[val.Length]; + val.CopyTo(Value, 0); + } + + public override void Serialize(BinaryWriter writer) + { + writer.WriteVarBytes(Value); + } + + public override void Deserialize(BinaryReader reader) + { + Value = reader.ReadVarBytes(); + } + } +} diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs new file mode 100644 index 0000000000..454d68d518 --- /dev/null +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -0,0 +1,104 @@ + +using System; + +namespace Neo.Trie.MPT +{ + public class MPTTrie: Trie + { + private MPTDatabase db; + private MPTNode root; + public MPTTrie(MPTDatabase db, byte[] root) + { + + } + + public MPTTrie(MPTDatabase db, MPTNode root) + { + this.db = db; + this.root = root; + } + + public byte[] GetRoot => Hash(); + + public MPTNode Resolve(byte[] hash) + { + return db.Node(hash); + } + + public byte[] Hash() + { + return new byte[]{}; + } + + public bool TryGet(byte[] path, out byte[] value) + { + return tryGet(ref root, path, out value); + } + + private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) + { + switch(node) + { + case ValueNode valueNode: + { + value = (byte[])valueNode.Value.Clone(); + return true; + } + case HashNode hashNode: + { + var result = false; + node = Resolve(hashNode.Hash); + result = tryGet(ref node, path, out value); + return result; + } + case BranchNode branchNode: + { + if (0 == path.Length) { + return tryGet(ref branchNode.Children[16], path, out value); + } + return tryGet(ref branchNode.Children[path[0]], path.Skip(1), out value); + } + case ExtensionNode extensionNode: + { + var prefix = extensionNode.Key.CommonPrefix(path); + if (prefix.Length == extensionNode.Key.Length) + { + return tryGet(ref extensionNode.Next, path.Skip(prefix.Length), out value); + } + break; + } + case LeafNode leafNode: + { + if (leafNode.Key.Equal(path)) + { + return tryGet(ref leafNode.Value, path, out value); + } + break; + } + } + value = new byte[]{}; + return false; + } + + public bool TryUpdate(byte[] path, byte[] value) + { + return true; + } + + private bool tryUpdate(byte[] path, byte[] value) + { + return true; + } + + public bool TryDelete(byte[] path) + { + return true; + } + + private bool tryDelete(byte[] path, out MPTNode newNode) + { + newNode = null; + return true; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs new file mode 100644 index 0000000000..68b4217972 --- /dev/null +++ b/src/neo/Trie/Trie.cs @@ -0,0 +1,8 @@ + +namespace Neo.Trie +{ + public abstract class Trie + { + + } +} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs new file mode 100644 index 0000000000..a76feb8e57 --- /dev/null +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -0,0 +1,26 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Trie.MPT; + +namespace Neo.UnitTests.Trie.MPT +{ + [TestClass] + public class UT_Helper + { + [TestMethod] + public void TestAdd() + { + var a = "ab".HexToBytes(); + byte b = 0x0c; + a.Add(b); + Assert.AreEqual("ab", a.ToHexString()); + } + + [TestMethod] + public void TestSkip() + { + var s = "abcd01".HexToBytes(); + s = s.Skip(2); + Assert.AreEqual("01", s.ToHexString()); + } + } +} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs new file mode 100644 index 0000000000..63754df43b --- /dev/null +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -0,0 +1,56 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Trie.MPT; + +namespace Neo.UnitTests.Trie.MPT +{ + [TestClass] + public class UT_MPTTrie + { + private static Node root; + + [ClassInitialize] + public static void ClassInit(TestContext context) + { + var r = new ExtensionNode(); + var b = new BranchNode(); + var l1 = new LeafNode(); + var l2 = new LeafNode(); + var v1 = new ValueNode("abcd".HexToBytes()); + var v2 = new ValueNode("2222".HexToBytes()); + r.Key = "0a0c".HexToBytes(); + l1.Key = new byte[]{0x01}; + l2.Key = new byte[]{0x09}; + r.Next = b; + b.Children[0] = l1; + l1.Value = v1; + b.Children[9] = l2; + l2.Value = v2; + root = r; + } + + [TestMethod] + public void TestTryGet() + { + var mpt = new MPTTrie(null, root); + var result = mpt.TryGet("0a0c0001".HexToBytes(), out byte[] value); + Assert.IsTrue(result); + Assert.AreEqual("abcd", value.ToHexString()); + + result = mpt.TryGet("0a0c0909".HexToBytes(), out value); + Assert.IsTrue(result); + Assert.AreEqual("2222", value.ToHexString()); + + result = mpt.TryGet("0a0b0909".HexToBytes(), out value); + Assert.IsFalse(result); + + result = mpt.TryGet("0a0c0309".HexToBytes(), out value); + Assert.IsFalse(result); + + result = mpt.TryGet("0a0c0002".HexToBytes(), out value); + Assert.IsFalse(result); + + result = mpt.TryGet("0a0c090901".HexToBytes(), out value); + Assert.IsFalse(result); + } + } +} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Node.cs b/tests/neo.UnitTests/Trie/MPT/UT_Node.cs new file mode 100644 index 0000000000..e69de29bb2 From 54613d3330742ce6c9448bbd519d592f9f7bd955 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 09:44:50 +0800 Subject: [PATCH 002/171] add TryDelete --- src/neo/Trie/MPT/Helper.cs | 11 +- src/neo/Trie/MPT/MPTDatabase.cs | 20 +- src/neo/Trie/MPT/MPTNode.cs | 252 +++++++++++++--- src/neo/Trie/MPT/MPTTrie.cs | 333 +++++++++++++++++---- src/neo/Trie/Trie.cs | 4 +- src/neo/Trie/{Database.cs => TrieDb.cs} | 2 +- tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 24 +- tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs | 27 ++ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 121 +++++++- tests/neo.UnitTests/Trie/MPT/UT_Node.cs | 0 10 files changed, 668 insertions(+), 126 deletions(-) rename src/neo/Trie/{Database.cs => TrieDb.cs} (55%) create mode 100644 tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs delete mode 100644 tests/neo.UnitTests/Trie/MPT/UT_Node.cs diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs index bf15117ef6..acab94f28c 100644 --- a/src/neo/Trie/MPT/Helper.cs +++ b/src/neo/Trie/MPT/Helper.cs @@ -17,12 +17,13 @@ public static byte[] CommonPrefix(this byte[] a, byte[] b) var prefix = new byte[]{}; var minLen = a.Length <= b.Length ? a.Length : b.Length; - if (a.Length == 0 || b.Length == 0 || a[0] != b[0]) return prefix; - - for (int i = 0; i < minLen; i++) + if (a.Length != 0 && b.Length != 0) { - if (a[i] != b[i]) break; - prefix = prefix.Add(a[i]); + for (int i = 0; i < minLen; i++) + { + if (a[i] != b[i]) break; + prefix = prefix.Add(a[i]); + } } return prefix; } diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 8aa5004f25..0601537db3 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,15 +1,17 @@ using Neo.Persistence; +using System.Threading; +using System; namespace Neo.Trie.MPT { - public class MPTDatabase: Database + public class MPTDatabase: ITrieDatabase { private IStore store; - private byte TABLE = 0x54; + public static readonly byte TABLE = 0x4D; - private byte[] GetStoreKey(byte[] hash) + private byte[] StoreKey(byte[] hash) { return hash; } @@ -21,9 +23,19 @@ public MPTDatabase(IStore store) public MPTNode Node(byte[] hash) { - var data = store.TryGet(TABLE, GetStoreKey(hash)); + var data = store.TryGet(TABLE, StoreKey(hash)); var n = MPTNode.Decode(data); return n; } + + public void Delete(byte[] hash) + { + store.Delete(TABLE, StoreKey(hash)); + } + + public void Put(MPTNode node) + { + store.Put(TABLE, StoreKey(node.GetHash()), node.Encode()); + } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 34a35fa48e..647fd1f07f 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -1,113 +1,221 @@ using System.IO; +using System.Text; using Neo.IO; +using Neo.IO.Json; using Neo.Cryptography; namespace Neo.Trie.MPT { - enum NodeType + public enum NodeType { - BranchNode, - ExtensionNode, - LeafNode, + FullNode, + ShortNode, HashNode, - ValueNode + ValueNode, + NullNode = 0xFF } + public class NodeFlag { public byte[] Hash; - public bool dirty; + public bool Dirty; + + public NodeFlag() + { + Dirty = true; + } } + public abstract class MPTNode: ISerializable { + public NodeFlag Flag; + protected NodeType nType; - public NodeFlag Flag { get; } + protected abstract byte[] calHash(); + + public virtual byte[] GetHash() { + if (!Flag.Dirty) return Flag.Hash; + Flag.Hash = calHash(); + Flag.Dirty = false; + return (byte[])Flag.Hash.Clone(); + } + + public void ResetFlag() + { + Flag = new NodeFlag(); + } public int Size { get; } + + public MPTNode() + { + Flag = new NodeFlag(); + } + + public virtual void Serialize(BinaryWriter writer) + { + writer.Write((byte)nType); + } + + public virtual void Deserialize(BinaryReader reader) + { + + } + + public byte[] Encode() + { + return this.ToArray(); + } public static MPTNode Decode(byte[] data) { var nodeType = (NodeType)data[0]; data = data.Skip(1); - switch (nodeType) + + using (MemoryStream ms = new MemoryStream(data, false)) + using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) { - case NodeType.BranchNode: - return BranchNode.Decode(data); - case NodeType.ExtensionNode: - return ExtensionNode.Decode(data); - case NodeType.LeafNode: - return LeafNode.Decode(data); - case NodeType.ValueNode: - return ValueNode.Decode(data); - default: - throw new System.Exception(); + switch (nodeType) + { + case NodeType.FullNode: + { + var n = new FullNode(); + n.Deserialize(reader); + return n; + } + case NodeType.ShortNode: + { + var n = new ShortNode(); + n.Deserialize(reader); + return n; + } + case NodeType.ValueNode: + { + var n = new ValueNode(); + n.Deserialize(reader); + return n; + } + default: + throw new System.Exception(); + } } } + public abstract JObject ToJson(); } - public class ExtensionNode : MPTNode + public class ShortNode : MPTNode { public byte[] Key; + public MPTNode Next; - public ExtensionNode Clone() + public new int Size => Key.Length + Next.Size; + + protected override byte[] calHash(){ + return Key.Concat(Next.GetHash()).Sha256(); + } + public ShortNode() + { + nType = NodeType.ShortNode; + } + + public ShortNode Clone() { - var cloned = new ExtensionNode(); - cloned.Key = (byte[])Key.Clone(); - cloned.Next = Next; + var cloned = new ShortNode() { + Key = (byte[])Key.Clone(), + Next = Next, + }; return cloned; } - public byte[] Encode() + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + writer.WriteVarBytes(Key); + writer.WriteVarBytes(Next.GetHash()); + } + + public override void Deserialize(BinaryReader reader) { - return new byte[]{}; + var hashNode = new HashNode(); + Key = reader.ReadVarBytes(); + hashNode.Deserialize(reader); + Next = hashNode; } - public new static ExtensionNode Decode(byte[] data) + public override JObject ToJson() { - var n = new ExtensionNode(); - return n; + var json = new JObject(); + json["key"] = Key.ToHexString(); + json["next"] = Next.ToJson(); + return json; } } - public class BranchNode : MPTNode + public class FullNode : MPTNode { public MPTNode[] Children = new MPTNode[17]; - public BranchNode Clone() + public new int Size; + + public FullNode() { - var cloned = new BranchNode(); + nType = NodeType.FullNode; for (int i = 0; i < Children.Length; i++) { - cloned.Children[i] = Children[i]; + Children[i] = HashNode.EmptyNode(); } - return cloned; } - public override void Serialize(BinaryWriter writer) + protected override byte[] calHash() { - writer.WriteNullableArray(Children); + var bytes = new byte[0]; + for (int i = 0; i < Children.Length; i++) + { + bytes = bytes.Concat(Children[i].GetHash()); + } + return bytes.Sha256(); } - public override void Deserialize(BinaryReader reader) + public FullNode Clone() { - + var cloned = new FullNode(); + for (int i = 0; i < Children.Length; i++) + { + cloned.Children[i] = Children[i]; + } + return cloned; } - } - - public class LeafNode : MPTNode - { - public byte[] Key; - public MPTNode Value; public override void Serialize(BinaryWriter writer) { - + base.Serialize(writer); + for (int i = 0; i < Children.Length; i++) + { + writer.WriteVarBytes(Children[i].GetHash()); + } } public override void Deserialize(BinaryReader reader) { - Value = new HashNode(reader.ReadVarBytes()); + for (int i = 0; i < Children.Length; i++) + { + var hashNode = new HashNode(reader.ReadVarBytes()); + Children[i] = hashNode; + } + } + + public override JObject ToJson() + { + var json = new JObject(); + var jchildren = new JArray(); + for (int i = 0; i < Children.Length; i++) + { + jchildren.Add(Children[i].ToJson()); + } + json["children"] = jchildren; + return json; } } @@ -115,14 +223,32 @@ public class HashNode : MPTNode { public byte[] Hash; + public HashNode() + { + nType = NodeType.HashNode; + } + public HashNode(byte[] hash) { - Hash = new byte[hash.Length]; - hash.CopyTo(Hash, 0); + nType = NodeType.HashNode; + Hash = (byte[])hash.Clone(); + } + + protected override byte[] calHash() + { + return (byte[])Hash.Clone(); + } + + public static HashNode EmptyNode() + { + return new HashNode(new byte[]{}); } + public bool IsEmptyNode => Hash.Length == 0; + public override void Serialize(BinaryWriter writer) { + base.Serialize(writer); writer.WriteVarBytes(Hash); } @@ -130,20 +256,41 @@ public override void Deserialize(BinaryReader reader) { Hash = reader.ReadVarBytes(); } + + public override JObject ToJson() + { + var json = new JObject(); + if (!this.IsEmptyNode) + { + json["hash"] = Hash.ToHexString(); + } + return json; + } } public class ValueNode : MPTNode { public byte[] Value; + protected override byte[] calHash() + { + return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); + } + + public ValueNode() + { + nType = NodeType.ValueNode; + } + public ValueNode(byte[] val) { - Value = new byte[val.Length]; - val.CopyTo(Value, 0); + nType = NodeType.ValueNode; + Value = (byte[])val.Clone(); } public override void Serialize(BinaryWriter writer) { + base.Serialize(writer); writer.WriteVarBytes(Value); } @@ -151,5 +298,12 @@ public override void Deserialize(BinaryReader reader) { Value = reader.ReadVarBytes(); } + + public override JObject ToJson() + { + var json = new JObject(); + json["value"] = Value.ToHexString(); + return json; + } } } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 454d68d518..c003c80f2b 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,104 +1,333 @@ - +using Neo.IO.Json; using System; namespace Neo.Trie.MPT { - public class MPTTrie: Trie + public class MPTTrie : ITrie { private MPTDatabase db; private MPTNode root; + public MPTTrie(MPTDatabase db, byte[] root) { - + if (db is null) + throw new System.Exception(); + this.db = db; + if (root.Length == 0) + { + this.root = HashNode.EmptyNode(); + } + else + { + this.root = Resolve(root); + } } - + public MPTTrie(MPTDatabase db, MPTNode root) { + if (db is null) + throw new System.Exception(); this.db = db; - this.root = root; + if (root is null) + { + this.root = HashNode.EmptyNode(); + } + else + { + this.root = root; + } } - public byte[] GetRoot => Hash(); - public MPTNode Resolve(byte[] hash) { return db.Node(hash); } - public byte[] Hash() - { - return new byte[]{}; - } - - public bool TryGet(byte[] path, out byte[] value) + public bool TryGet(byte[] path, out byte[] value) { return tryGet(ref root, path, out value); } private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) { - switch(node) + switch (node) { case ValueNode valueNode: - { - value = (byte[])valueNode.Value.Clone(); - return true; - } + { + if (path.Length == 0) + { + value = (byte[])valueNode.Value.Clone(); + return true; + } + break; + } case HashNode hashNode: - { - var result = false; - node = Resolve(hashNode.Hash); - result = tryGet(ref node, path, out value); - return result; - } - case BranchNode branchNode: - { - if (0 == path.Length) { - return tryGet(ref branchNode.Children[16], path, out value); + { + if (hashNode.IsEmptyNode) break; + node = Resolve(hashNode.Hash); + return tryGet(ref node, path, out value); } - return tryGet(ref branchNode.Children[path[0]], path.Skip(1), out value); - } - case ExtensionNode extensionNode: - { - var prefix = extensionNode.Key.CommonPrefix(path); - if (prefix.Length == extensionNode.Key.Length) + case FullNode fullNode: { - return tryGet(ref extensionNode.Next, path.Skip(prefix.Length), out value); + if (path.Length == 0) + { + return tryGet(ref fullNode.Children[16], path, out value); + } + return tryGet(ref fullNode.Children[path[0]], path.Skip(1), out value); } - break; - } - case LeafNode leafNode: - { - if (leafNode.Key.Equal(path)) + case ShortNode shortNode: { - return tryGet(ref leafNode.Value, path, out value); + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + return tryGet(ref shortNode.Next, path.Skip(prefix.Length), out value); + } + break; } - break; - } } - value = new byte[]{}; + value = new byte[] { }; return false; } - public bool TryUpdate(byte[] path, byte[] value) + public bool Put(byte[] path, byte[] value) { - return true; + var n = new ValueNode(value); + path = (byte[])path.Clone(); + if (0 == value.Length) + { + return tryDelete(ref root, path); + } + return put(ref root, path, n); } - private bool tryUpdate(byte[] path, byte[] value) + private bool put(ref MPTNode node, byte[] path, MPTNode val) { - return true; + var result = false; + var oldHash = node.GetHash(); + switch (node) + { + case ValueNode valueNode: + { + if (path.Length == 0 && val is ValueNode vn) + { + node = val; + node.ResetFlag(); + db.Put(node); + result = true; + break; + } + break; + } + case ShortNode shortNode: + { + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + result = put(ref shortNode.Next, path.Skip(prefix.Length), val); + if (result) + { + shortNode.ResetFlag(); + db.Put(shortNode); + } + break; + } + + var pathRemain = path.Skip(prefix.Length); + var keyRemain = shortNode.Key.Skip(prefix.Length); + var son = new FullNode(); + MPTNode grandSon1 = HashNode.EmptyNode(), grandSon2 = HashNode.EmptyNode(); + put(ref grandSon1, keyRemain.Skip(1), shortNode.Next); + son.Children[keyRemain[0]] = grandSon1; + if (pathRemain.Length == 0) + { + put(ref grandSon2, pathRemain, val); + son.Children[son.Children.Length] = grandSon2; + } + else + { + put(ref grandSon2, pathRemain.Skip(1), val); + son.Children[pathRemain[0]] = grandSon2; + } + db.Put(son); + if (0 < prefix.Length) + { + var extensionNode = new ShortNode() + { + Key = prefix, + Next = son, + }; + node = extensionNode; + db.Put(node); + } + else + { + node = son; + } + result = true; + break; + } + case FullNode fullNode: + { + if (path.Length == 0) + { + result = put(ref fullNode.Children[fullNode.Children.Length], path, val); + } + else + { + result = put(ref fullNode.Children[path[0]], path.Skip(1), val); + } + if (result) + { + fullNode.ResetFlag(); + db.Put(fullNode); + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) + { + var newNode = new ShortNode() + { + Key = path, + Next = val, + }; + node = newNode; + db.Put(node); + result = true; + break; + } + node = Resolve(hashNode.Hash); + result = put(ref node, path, val); + break; + } + default: + throw new System.Exception(); + } + if (result) db.Delete(oldHash); + return result; } public bool TryDelete(byte[] path) { - return true; + return tryDelete(ref root, path); + } + + private bool tryDelete(ref MPTNode node, byte[] path) + { + var result = false; + var oldHash = node.GetHash(); + + switch (node) + { + case ValueNode valueNode: + { + if (path.Length == 0) + { + node = HashNode.EmptyNode(); + result = true; + break; + } + break; + } + case ShortNode shortNode: + { + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + result = tryDelete(ref shortNode.Next, path.Skip(prefix.Length)); + if (!result) break; + if (shortNode.Next is HashNode hashNode && hashNode.IsEmptyNode) + { + node = shortNode.Next; + db.Put(node); + } + if (shortNode.Next is ShortNode sn) + { + shortNode.Key = shortNode.Key.Concat(sn.Key); + shortNode.Next = sn.Next; + shortNode.ResetFlag(); + db.Put(shortNode); + } + result = true; + break; + } + result = false; + break; + } + case FullNode fullNode: + { + if (path.Length == 0) + { + result = tryDelete(ref fullNode.Children[fullNode.Children.Length], path); + } + else + { + result = tryDelete(ref fullNode.Children[path[0]], path.Skip(1)); + } + if (!result) break; + var nonEmptyChildren = new byte[] { }; + for (int i = 0; i < fullNode.Children.Length; i++) + { + if (fullNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; + nonEmptyChildren = nonEmptyChildren.Add((byte)i); + } + if (1 < nonEmptyChildren.Length) + { + fullNode.ResetFlag(); + db.Put(fullNode); + break; + } + var childIndex = nonEmptyChildren[0]; + var child = fullNode.Children[childIndex]; + if (child is HashNode hashNode) child = Resolve(hashNode.Hash); + if (child is ShortNode shortNode) + { + db.Delete(shortNode.GetHash()); + shortNode.Key = nonEmptyChildren.Concat(shortNode.Key); + db.Put(shortNode); + node = child; + break; + } + var newNode = new ShortNode() + { + Key = nonEmptyChildren, + Next = child, + }; + node = newNode; + db.Put(node); + result = true; + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) + { + result = false; + break; + } + node = Resolve(hashNode.Hash); + result = tryDelete(ref node, path); + break; + } + } + if (result) db.Delete(oldHash); + return result; + } + + public byte[] GetRoot() + { + return this.root.GetHash(); + } + + public void Proof(byte[] path) + { + } - private bool tryDelete(byte[] path, out MPTNode newNode) + public JObject ToJson() { - newNode = null; - return true; + return root.ToJson(); } } } \ No newline at end of file diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index 68b4217972..4ee04fff7e 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -1,8 +1,8 @@ namespace Neo.Trie { - public abstract class Trie + public interface ITrie { - + } } \ No newline at end of file diff --git a/src/neo/Trie/Database.cs b/src/neo/Trie/TrieDb.cs similarity index 55% rename from src/neo/Trie/Database.cs rename to src/neo/Trie/TrieDb.cs index ff16c9e0de..06f798eeea 100644 --- a/src/neo/Trie/Database.cs +++ b/src/neo/Trie/TrieDb.cs @@ -1,7 +1,7 @@ namespace Neo.Trie { - public abstract class Database + public interface ITrieDatabase { } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs index a76feb8e57..1cf88b8bf9 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -6,13 +6,27 @@ namespace Neo.UnitTests.Trie.MPT [TestClass] public class UT_Helper { + [TestMethod] + public void TestConcat() + { + var a = new byte[]{0x01}; + var b = new byte[]{0x02}; + a = a.Concat(b); + Assert.AreEqual(2, a.Length); + } + [TestMethod] public void TestAdd() { var a = "ab".HexToBytes(); byte b = 0x0c; - a.Add(b); - Assert.AreEqual("ab", a.ToHexString()); + a = a.Add(b); + Assert.AreEqual("ab0c", a.ToHexString()); + + a = new byte[0]; + a = a.Add(b); + Assert.AreEqual(1, a.Length); + Assert.AreEqual("0c", a.ToHexString()); } [TestMethod] @@ -21,6 +35,12 @@ public void TestSkip() var s = "abcd01".HexToBytes(); s = s.Skip(2); Assert.AreEqual("01", s.ToHexString()); + + s = new byte[]{0x01}; + s = s.Skip(1); + Assert.AreEqual(0, s.Length); + s = s.Skip(2); + Assert.AreEqual(0, s.Length); } } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs new file mode 100644 index 0000000000..b54cfe574f --- /dev/null +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs @@ -0,0 +1,27 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Trie.MPT; +using System.Text; + +namespace Neo.UnitTests.Trie.MPT +{ + [TestClass] + public class UT_MPTNode + { + [TestMethod] + public void TestDecode() + { + var n = new ValueNode(); + n.Value = Encoding.ASCII.GetBytes("hello"); + var code = n.Encode(); + var m = MPTNode.Decode(code); + Assert.IsInstanceOfType(m, n.GetType()); + } + + [TestMethod] + public void TestFlag() + { + var n = new ShortNode(); + Assert.IsTrue(n.Flag.Dirty); + } + } +} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 63754df43b..9f9ddfb568 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -1,37 +1,58 @@ +using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Trie.MPT; +using Neo.Persistence; +using Neo.IO; +using System; namespace Neo.UnitTests.Trie.MPT { [TestClass] public class UT_MPTTrie { - private static Node root; + private MPTNode root; [ClassInitialize] public static void ClassInit(TestContext context) { - var r = new ExtensionNode(); - var b = new BranchNode(); - var l1 = new LeafNode(); - var l2 = new LeafNode(); - var v1 = new ValueNode("abcd".HexToBytes()); - var v2 = new ValueNode("2222".HexToBytes()); + + } + + [TestInitialize] + public void TestInit() + { + var r = new ShortNode(); r.Key = "0a0c".HexToBytes(); + var b = new FullNode(); + var l1 = new ShortNode(); l1.Key = new byte[]{0x01}; + var l2 = new ShortNode(); l2.Key = new byte[]{0x09}; + var v1 = new ValueNode(); + v1.Value = "abcd".HexToBytes(); + var v2 = new ValueNode(); + v2.Value = "2222".HexToBytes(); + var h1 = new HashNode(); + h1.Hash = Encoding.ASCII.GetBytes("hello"); + var l3 = new ShortNode(); + l3.Next = h1; + l3.Key = "0e".HexToBytes(); + r.Next = b; b.Children[0] = l1; - l1.Value = v1; + l1.Next = v1; b.Children[9] = l2; - l2.Value = v2; - root = r; + l2.Next = v2; + b.Children[10] = l3; + root = r; } [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(null, root); + var store = new MemoryStore(); + var mptdb = new MPTDatabase(store); + var mpt = new MPTTrie(mptdb, root); var result = mpt.TryGet("0a0c0001".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.AreEqual("abcd", value.ToHexString()); @@ -50,7 +71,85 @@ public void TestTryGet() Assert.IsFalse(result); result = mpt.TryGet("0a0c090901".HexToBytes(), out value); + Assert.AreEqual(false, result); + } + + [TestMethod] + public void TestTryGetResolve() + { + var n = new ValueNode(); + n.Value = Encoding.ASCII.GetBytes("hello"); + var store = new MemoryStore(); + store.Put(MPTDatabase.TABLE, n.GetHash(), n.Encode()); + var mptdb = new MPTDatabase(store); + var mpt = new MPTTrie(mptdb, root); + var result = mpt.TryGet("0a0c0a0e".HexToBytes(), out byte[] value); + + Assert.IsTrue(result); + Assert.IsTrue(value.Equal(n.Value)); + } + + [TestMethod] + public void TestTryPut() + { + var store = new MemoryStore(); + var mptdb = new MPTDatabase(store); + var sn = (ShortNode)root; + Assert.IsTrue(root is ShortNode); + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", sn.GetHash().ToHexString()); + var mpt = new MPTTrie(mptdb, new byte[]{}); + mpt.Put("0a0c0001".HexToBytes(), "abcd".HexToBytes()); + mpt.Put("0a0c0909".HexToBytes(), "2222".HexToBytes()); + Assert.AreEqual("{\"key\":\"0a0c\",\"next\":{\"children\":[{\"key\":\"01\",\"next\":{\"value\":\"abcd\"}},{},{},{},{},{},{},{},{},{\"key\":\"09\",\"next\":{\"value\":\"2222\"}},{},{},{},{},{},{},{}]}}", mpt.ToJson().ToString()); + mpt.Put("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); + Assert.AreEqual(8, store.Size(MPTDatabase.TABLE)); + } + + [TestMethod] + public void TestTryDelete() + { + var store = new MemoryStore(); + var mptdb = new MPTDatabase(store); + + var r1 = new ShortNode(); + r1.Key = "0a0c0001".HexToBytes(); + + + var r = new ShortNode(); + r.Key = "0a0c".HexToBytes(); + + + var b = new FullNode(); + r.Next = b; + + var l1 = new ShortNode(); + l1.Key = new byte[]{0x01}; + var v1 = new ValueNode(); + v1.Value = "abcd".HexToBytes(); + l1.Next = v1; + b.Children[0] = l1; + + var l2 = new ShortNode(); + l2.Key = new byte[]{0x09}; + var v2 = new ValueNode(); + v2.Value = "2222".HexToBytes(); + l2.Next = v2; + b.Children[9] = l2; + + r1.Next = v1; + Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", r1.GetHash().ToHexString()); + Assert.AreEqual("f3ad94e8fb6e1e85a8b573b2343845e3b0e0b96b61fcd0e20b6df159fde137a7", r.GetHash().ToHexString()); + + var mpt = new MPTTrie(mptdb, r); + var result = true; + result = mpt.TryGet("0a0c0909".HexToBytes(), out byte[] value); + Assert.IsTrue(result); + result = mpt.TryDelete("0a0c0909".HexToBytes()); + Assert.IsTrue(result); + result = mpt.TryDelete("0a0c0a0e".HexToBytes()); Assert.IsFalse(result); + Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", mpt.GetRoot().ToHexString()); } } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Node.cs b/tests/neo.UnitTests/Trie/MPT/UT_Node.cs deleted file mode 100644 index e69de29bb2..0000000000 From bec65ef5593abfe1f9c537e07c8d2342f60e7178 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 10:32:57 +0800 Subject: [PATCH 003/171] split nodes --- src/neo/Trie/MPT/MPTDatabase.cs | 6 +- src/neo/Trie/MPT/MPTNode.cs | 309 --------------------- src/neo/Trie/MPT/MPTNode/FullNode.cs | 73 +++++ src/neo/Trie/MPT/MPTNode/HashNode.cs | 55 ++++ src/neo/Trie/MPT/MPTNode/MPTNode.cs | 106 +++++++ src/neo/Trie/MPT/MPTNode/ShortNode.cs | 56 ++++ src/neo/Trie/MPT/MPTNode/ValueNode.cs | 46 +++ src/neo/Trie/MPT/MPTTrie.cs | 10 +- src/neo/Trie/Trie.cs | 12 +- src/neo/Trie/TrieDb.cs | 8 - tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 9 +- 11 files changed, 364 insertions(+), 326 deletions(-) delete mode 100644 src/neo/Trie/MPT/MPTNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/FullNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/HashNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/MPTNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/ShortNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/ValueNode.cs delete mode 100644 src/neo/Trie/TrieDb.cs diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 0601537db3..3a34a60ce7 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,11 +1,9 @@ using Neo.Persistence; -using System.Threading; -using System; namespace Neo.Trie.MPT { - public class MPTDatabase: ITrieDatabase + public class MPTDatabase { private IStore store; @@ -34,7 +32,7 @@ public void Delete(byte[] hash) } public void Put(MPTNode node) - { + { store.Put(TABLE, StoreKey(node.GetHash()), node.Encode()); } } diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs deleted file mode 100644 index 647fd1f07f..0000000000 --- a/src/neo/Trie/MPT/MPTNode.cs +++ /dev/null @@ -1,309 +0,0 @@ -using System.IO; -using System.Text; -using Neo.IO; -using Neo.IO.Json; -using Neo.Cryptography; - -namespace Neo.Trie.MPT -{ - public enum NodeType - { - FullNode, - ShortNode, - HashNode, - ValueNode, - NullNode = 0xFF - } - - public class NodeFlag - { - public byte[] Hash; - public bool Dirty; - - public NodeFlag() - { - Dirty = true; - } - } - - public abstract class MPTNode: ISerializable - { - public NodeFlag Flag; - protected NodeType nType; - - protected abstract byte[] calHash(); - - public virtual byte[] GetHash() { - if (!Flag.Dirty) return Flag.Hash; - Flag.Hash = calHash(); - Flag.Dirty = false; - return (byte[])Flag.Hash.Clone(); - } - - public void ResetFlag() - { - Flag = new NodeFlag(); - } - - public int Size { get; } - - public MPTNode() - { - Flag = new NodeFlag(); - } - - public virtual void Serialize(BinaryWriter writer) - { - writer.Write((byte)nType); - } - - public virtual void Deserialize(BinaryReader reader) - { - - } - - public byte[] Encode() - { - return this.ToArray(); - } - - public static MPTNode Decode(byte[] data) - { - var nodeType = (NodeType)data[0]; - data = data.Skip(1); - - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) - { - switch (nodeType) - { - case NodeType.FullNode: - { - var n = new FullNode(); - n.Deserialize(reader); - return n; - } - case NodeType.ShortNode: - { - var n = new ShortNode(); - n.Deserialize(reader); - return n; - } - case NodeType.ValueNode: - { - var n = new ValueNode(); - n.Deserialize(reader); - return n; - } - default: - throw new System.Exception(); - } - } - } - - public abstract JObject ToJson(); - } - - public class ShortNode : MPTNode - { - public byte[] Key; - - public MPTNode Next; - - public new int Size => Key.Length + Next.Size; - - protected override byte[] calHash(){ - return Key.Concat(Next.GetHash()).Sha256(); - } - public ShortNode() - { - nType = NodeType.ShortNode; - } - - public ShortNode Clone() - { - var cloned = new ShortNode() { - Key = (byte[])Key.Clone(), - Next = Next, - }; - return cloned; - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.WriteVarBytes(Key); - writer.WriteVarBytes(Next.GetHash()); - } - - public override void Deserialize(BinaryReader reader) - { - var hashNode = new HashNode(); - Key = reader.ReadVarBytes(); - hashNode.Deserialize(reader); - Next = hashNode; - } - - public override JObject ToJson() - { - var json = new JObject(); - json["key"] = Key.ToHexString(); - json["next"] = Next.ToJson(); - return json; - } - } - - public class FullNode : MPTNode - { - public MPTNode[] Children = new MPTNode[17]; - - public new int Size; - - public FullNode() - { - nType = NodeType.FullNode; - for (int i = 0; i < Children.Length; i++) - { - Children[i] = HashNode.EmptyNode(); - } - } - - protected override byte[] calHash() - { - var bytes = new byte[0]; - for (int i = 0; i < Children.Length; i++) - { - bytes = bytes.Concat(Children[i].GetHash()); - } - return bytes.Sha256(); - } - - public FullNode Clone() - { - var cloned = new FullNode(); - for (int i = 0; i < Children.Length; i++) - { - cloned.Children[i] = Children[i]; - } - return cloned; - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - for (int i = 0; i < Children.Length; i++) - { - writer.WriteVarBytes(Children[i].GetHash()); - } - } - - public override void Deserialize(BinaryReader reader) - { - for (int i = 0; i < Children.Length; i++) - { - var hashNode = new HashNode(reader.ReadVarBytes()); - Children[i] = hashNode; - } - } - - public override JObject ToJson() - { - var json = new JObject(); - var jchildren = new JArray(); - for (int i = 0; i < Children.Length; i++) - { - jchildren.Add(Children[i].ToJson()); - } - json["children"] = jchildren; - return json; - } - } - - public class HashNode : MPTNode - { - public byte[] Hash; - - public HashNode() - { - nType = NodeType.HashNode; - } - - public HashNode(byte[] hash) - { - nType = NodeType.HashNode; - Hash = (byte[])hash.Clone(); - } - - protected override byte[] calHash() - { - return (byte[])Hash.Clone(); - } - - public static HashNode EmptyNode() - { - return new HashNode(new byte[]{}); - } - - public bool IsEmptyNode => Hash.Length == 0; - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.WriteVarBytes(Hash); - } - - public override void Deserialize(BinaryReader reader) - { - Hash = reader.ReadVarBytes(); - } - - public override JObject ToJson() - { - var json = new JObject(); - if (!this.IsEmptyNode) - { - json["hash"] = Hash.ToHexString(); - } - return json; - } - } - - public class ValueNode : MPTNode - { - public byte[] Value; - - protected override byte[] calHash() - { - return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); - } - - public ValueNode() - { - nType = NodeType.ValueNode; - } - - public ValueNode(byte[] val) - { - nType = NodeType.ValueNode; - Value = (byte[])val.Clone(); - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.WriteVarBytes(Value); - } - - public override void Deserialize(BinaryReader reader) - { - Value = reader.ReadVarBytes(); - } - - public override JObject ToJson() - { - var json = new JObject(); - json["value"] = Value.ToHexString(); - return json; - } - } -} diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs new file mode 100644 index 0000000000..9dba385192 --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -0,0 +1,73 @@ +using System.IO; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public class FullNode : MPTNode + { + public MPTNode[] Children = new MPTNode[17]; + + public new int Size; + + public FullNode() + { + nType = NodeType.FullNode; + for (int i = 0; i < Children.Length; i++) + { + Children[i] = HashNode.EmptyNode(); + } + } + + protected override byte[] calHash() + { + var bytes = new byte[0]; + for (int i = 0; i < Children.Length; i++) + { + bytes = bytes.Concat(Children[i].GetHash()); + } + return bytes.Sha256(); + } + + public FullNode Clone() + { + var cloned = new FullNode(); + for (int i = 0; i < Children.Length; i++) + { + cloned.Children[i] = Children[i]; + } + return cloned; + } + + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + for (int i = 0; i < Children.Length; i++) + { + writer.WriteVarBytes(Children[i].GetHash()); + } + } + + public override void Deserialize(BinaryReader reader) + { + for (int i = 0; i < Children.Length; i++) + { + var hashNode = new HashNode(reader.ReadVarBytes()); + Children[i] = hashNode; + } + } + + public override JObject ToJson() + { + var json = new JObject(); + var jchildren = new JArray(); + for (int i = 0; i < Children.Length; i++) + { + jchildren.Add(Children[i].ToJson()); + } + json["children"] = jchildren; + return json; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs new file mode 100644 index 0000000000..f4cb71ea61 --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -0,0 +1,55 @@ +using System.IO; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public class HashNode : MPTNode + { + public byte[] Hash; + + public HashNode() + { + nType = NodeType.HashNode; + } + + public HashNode(byte[] hash) + { + nType = NodeType.HashNode; + Hash = (byte[])hash.Clone(); + } + + protected override byte[] calHash() + { + return (byte[])Hash.Clone(); + } + + public static HashNode EmptyNode() + { + return new HashNode(new byte[]{}); + } + + public bool IsEmptyNode => Hash.Length == 0; + + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + writer.WriteVarBytes(Hash); + } + + public override void Deserialize(BinaryReader reader) + { + Hash = reader.ReadVarBytes(); + } + + public override JObject ToJson() + { + var json = new JObject(); + if (!this.IsEmptyNode) + { + json["hash"] = Hash.ToHexString(); + } + return json; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/MPTNode.cs b/src/neo/Trie/MPT/MPTNode/MPTNode.cs new file mode 100644 index 0000000000..104b57099c --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/MPTNode.cs @@ -0,0 +1,106 @@ +using System.IO; +using System.Text; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public enum NodeType + { + FullNode, + ShortNode, + HashNode, + ValueNode, + NullNode = 0xFF + } + + public class NodeFlag + { + public byte[] Hash; + public bool Dirty; + + public NodeFlag() + { + Dirty = true; + } + } + + public abstract class MPTNode: ISerializable + { + public NodeFlag Flag; + protected NodeType nType; + + protected abstract byte[] calHash(); + + public virtual byte[] GetHash() { + if (!Flag.Dirty) return Flag.Hash; + Flag.Hash = calHash(); + Flag.Dirty = false; + return (byte[])Flag.Hash.Clone(); + } + + public void ResetFlag() + { + Flag = new NodeFlag(); + } + + public int Size { get; } + + public MPTNode() + { + Flag = new NodeFlag(); + } + + public virtual void Serialize(BinaryWriter writer) + { + writer.Write((byte)nType); + } + + public virtual void Deserialize(BinaryReader reader) + { + + } + + public byte[] Encode() + { + return this.ToArray(); + } + + public static MPTNode Decode(byte[] data) + { + var nodeType = (NodeType)data[0]; + data = data.Skip(1); + + using (MemoryStream ms = new MemoryStream(data, false)) + using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) + { + switch (nodeType) + { + case NodeType.FullNode: + { + var n = new FullNode(); + n.Deserialize(reader); + return n; + } + case NodeType.ShortNode: + { + var n = new ShortNode(); + n.Deserialize(reader); + return n; + } + case NodeType.ValueNode: + { + var n = new ValueNode(); + n.Deserialize(reader); + return n; + } + default: + throw new System.Exception(); + } + } + } + + public abstract JObject ToJson(); + } +} diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs new file mode 100644 index 0000000000..87e63febd2 --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -0,0 +1,56 @@ +using System.IO; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public class ShortNode : MPTNode + { + public byte[] Key; + + public MPTNode Next; + + public new int Size => Key.Length + Next.Size; + + protected override byte[] calHash(){ + return Key.Concat(Next.GetHash()).Sha256(); + } + public ShortNode() + { + nType = NodeType.ShortNode; + } + + public ShortNode Clone() + { + var cloned = new ShortNode() { + Key = (byte[])Key.Clone(), + Next = Next, + }; + return cloned; + } + + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + writer.WriteVarBytes(Key); + writer.WriteVarBytes(Next.GetHash()); + } + + public override void Deserialize(BinaryReader reader) + { + var hashNode = new HashNode(); + Key = reader.ReadVarBytes(); + hashNode.Deserialize(reader); + Next = hashNode; + } + + public override JObject ToJson() + { + var json = new JObject(); + json["key"] = Key.ToHexString(); + json["next"] = Next.ToJson(); + return json; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs new file mode 100644 index 0000000000..b4c84cbe04 --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -0,0 +1,46 @@ +using System.IO; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public class ValueNode : MPTNode + { + public byte[] Value; + + protected override byte[] calHash() + { + return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); + } + + public ValueNode() + { + nType = NodeType.ValueNode; + } + + public ValueNode(byte[] val) + { + nType = NodeType.ValueNode; + Value = (byte[])val.Clone(); + } + + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + writer.WriteVarBytes(Value); + } + + public override void Deserialize(BinaryReader reader) + { + Value = reader.ReadVarBytes(); + } + + public override JObject ToJson() + { + var json = new JObject(); + json["value"] = Value.ToHexString(); + return json; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index c003c80f2b..cc2c1bc2dd 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,5 +1,4 @@ using Neo.IO.Json; -using System; namespace Neo.Trie.MPT { @@ -89,7 +88,7 @@ private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) return false; } - public bool Put(byte[] path, byte[] value) + public bool TryPut(byte[] path, byte[] value) { var n = new ValueNode(value); path = (byte[])path.Clone(); @@ -320,9 +319,14 @@ public byte[] GetRoot() return this.root.GetHash(); } - public void Proof(byte[] path) + public bool Prove(byte[] key, byte[] proof) { + return true; + } + public byte[] GetProof(byte[] key, byte[] value) + { + return new byte[0]; } public JObject ToJson() diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index 4ee04fff7e..2c0919922f 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -3,6 +3,16 @@ namespace Neo.Trie { public interface ITrie { - + bool TryGet(byte[] path, out byte[] value); + + bool TryPut(byte[] path, byte[] value); + + bool TryDelete(byte[] path); + + byte[] GetRoot(); + + bool Prove(byte[] key, byte[] proof); + + byte[] GetProof(byte[] Key, byte [] value); } } \ No newline at end of file diff --git a/src/neo/Trie/TrieDb.cs b/src/neo/Trie/TrieDb.cs deleted file mode 100644 index 06f798eeea..0000000000 --- a/src/neo/Trie/TrieDb.cs +++ /dev/null @@ -1,8 +0,0 @@ - -namespace Neo.Trie -{ - public interface ITrieDatabase - { - - } -} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 9f9ddfb568..8d615e1281 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -103,7 +103,6 @@ public void TestTryPut() Assert.AreEqual("{\"key\":\"0a0c\",\"next\":{\"children\":[{\"key\":\"01\",\"next\":{\"value\":\"abcd\"}},{},{},{},{},{},{},{},{},{\"key\":\"09\",\"next\":{\"value\":\"2222\"}},{},{},{},{},{},{},{}]}}", mpt.ToJson().ToString()); mpt.Put("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); - Assert.AreEqual(8, store.Size(MPTDatabase.TABLE)); } [TestMethod] @@ -151,5 +150,13 @@ public void TestTryDelete() Assert.IsFalse(result); Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", mpt.GetRoot().ToHexString()); } + + [TestMethod] + public void TestGetProof() + { + var store = new MemoryStore(); + var mptdb = new MPTDatabase(store); + var mpt = new MPTTrie(mptdb, new byte[]{}); + } } } \ No newline at end of file From a991041656945d6812f974f03ee4195bc07b8dec Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 15:13:20 +0800 Subject: [PATCH 004/171] add GetProof and ut --- src/neo/Trie/MPT/MPTDatabase.cs | 5 +- src/neo/Trie/MPT/{MPTNode => }/MPTNode.cs | 47 +++++++++---------- src/neo/Trie/MPT/MPTNode/HashNode.cs | 11 ----- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 3 +- src/neo/Trie/MPT/MPTTrie.cs | 53 ++++++++++++++++++++-- src/neo/Trie/Trie.cs | 6 +-- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 42 +++++++++++++++-- 7 files changed, 117 insertions(+), 50 deletions(-) rename src/neo/Trie/MPT/{MPTNode => }/MPTNode.cs (70%) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 3a34a60ce7..cf9efcb00b 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,5 +1,6 @@ using Neo.Persistence; +using System.Text; namespace Neo.Trie.MPT { @@ -9,9 +10,11 @@ public class MPTDatabase public static readonly byte TABLE = 0x4D; + public static readonly byte[] Prefix = Encoding.ASCII.GetBytes("MPT"); + private byte[] StoreKey(byte[] hash) { - return hash; + return Prefix.Concat(hash); } public MPTDatabase(IStore store) diff --git a/src/neo/Trie/MPT/MPTNode/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs similarity index 70% rename from src/neo/Trie/MPT/MPTNode/MPTNode.cs rename to src/neo/Trie/MPT/MPTNode.cs index 104b57099c..84f62b2107 100644 --- a/src/neo/Trie/MPT/MPTNode/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -25,15 +25,16 @@ public NodeFlag() Dirty = true; } } - - public abstract class MPTNode: ISerializable + + public abstract class MPTNode : ISerializable { public NodeFlag Flag; protected NodeType nType; protected abstract byte[] calHash(); - - public virtual byte[] GetHash() { + + public virtual byte[] GetHash() + { if (!Flag.Dirty) return Flag.Hash; Flag.Hash = calHash(); Flag.Dirty = false; @@ -46,22 +47,22 @@ public void ResetFlag() } public int Size { get; } - + public MPTNode() { Flag = new NodeFlag(); } - public virtual void Serialize(BinaryWriter writer) + public virtual void Serialize(BinaryWriter writer) { writer.Write((byte)nType); } public virtual void Deserialize(BinaryReader reader) { - + } - + public byte[] Encode() { return this.ToArray(); @@ -78,23 +79,23 @@ public static MPTNode Decode(byte[] data) switch (nodeType) { case NodeType.FullNode: - { - var n = new FullNode(); - n.Deserialize(reader); - return n; - } + { + var n = new FullNode(); + n.Deserialize(reader); + return n; + } case NodeType.ShortNode: - { - var n = new ShortNode(); - n.Deserialize(reader); - return n; - } + { + var n = new ShortNode(); + n.Deserialize(reader); + return n; + } case NodeType.ValueNode: - { - var n = new ValueNode(); - n.Deserialize(reader); - return n; - } + { + var n = new ValueNode(); + n.Deserialize(reader); + return n; + } default: throw new System.Exception(); } diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index f4cb71ea61..057ffa25f6 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -31,17 +31,6 @@ public static HashNode EmptyNode() public bool IsEmptyNode => Hash.Length == 0; - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.WriteVarBytes(Hash); - } - - public override void Deserialize(BinaryReader reader) - { - Hash = reader.ReadVarBytes(); - } - public override JObject ToJson() { var json = new JObject(); diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 87e63febd2..5564e2bbcf 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -39,9 +39,8 @@ public override void Serialize(BinaryWriter writer) public override void Deserialize(BinaryReader reader) { - var hashNode = new HashNode(); Key = reader.ReadVarBytes(); - hashNode.Deserialize(reader); + var hashNode = new HashNode(reader.ReadVarBytes()); Next = hashNode; } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index cc2c1bc2dd..8c42ebd631 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,4 +1,5 @@ using Neo.IO.Json; +using System.Collections.Generic; namespace Neo.Trie.MPT { @@ -225,7 +226,6 @@ private bool tryDelete(ref MPTNode node, byte[] path) { node = HashNode.EmptyNode(); result = true; - break; } break; } @@ -319,14 +319,57 @@ public byte[] GetRoot() return this.root.GetHash(); } - public bool Prove(byte[] key, byte[] proof) + public Dictionary GetProof(byte[] path) { - return true; + var dict = new Dictionary { }; + getProof(ref root, path, dict); + return dict; } - public byte[] GetProof(byte[] key, byte[] value) + private void getProof(ref MPTNode node, byte[] path, Dictionary dict) { - return new byte[0]; + switch (node) + { + case ValueNode valueNode: + { + if (path.Length == 0) + { + dict.Add(valueNode.GetHash(), valueNode.Encode()); + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) break; + node = Resolve(hashNode.Hash); + dict.Add(node.GetHash(), node.Encode()); + getProof(ref node, path, dict); + break; + } + case FullNode fullNode: + { + dict.Add(fullNode.GetHash(), fullNode.Encode()); + if (path.Length == 0) + { + getProof(ref fullNode.Children[16], path, dict); + } + else + { + getProof(ref fullNode.Children[path[0]], path.Skip(1), dict); + } + break; + } + case ShortNode shortNode: + { + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + dict.Add(shortNode.GetHash(), shortNode.Encode()); + getProof(ref shortNode.Next, path.Skip(prefix.Length), dict); + } + break; + } + } } public JObject ToJson() diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index 2c0919922f..37c059b245 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -1,4 +1,6 @@ +using System.Collections.Generic; + namespace Neo.Trie { public interface ITrie @@ -11,8 +13,6 @@ public interface ITrie byte[] GetRoot(); - bool Prove(byte[] key, byte[] proof); - - byte[] GetProof(byte[] Key, byte [] value); + Dictionary GetProof(byte[] Key); } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 8d615e1281..6c8f35f868 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -98,10 +98,9 @@ public void TestTryPut() Assert.IsTrue(root is ShortNode); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", sn.GetHash().ToHexString()); var mpt = new MPTTrie(mptdb, new byte[]{}); - mpt.Put("0a0c0001".HexToBytes(), "abcd".HexToBytes()); - mpt.Put("0a0c0909".HexToBytes(), "2222".HexToBytes()); - Assert.AreEqual("{\"key\":\"0a0c\",\"next\":{\"children\":[{\"key\":\"01\",\"next\":{\"value\":\"abcd\"}},{},{},{},{},{},{},{},{},{\"key\":\"09\",\"next\":{\"value\":\"2222\"}},{},{},{},{},{},{},{}]}}", mpt.ToJson().ToString()); - mpt.Put("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); + mpt.TryPut("0a0c0001".HexToBytes(), "abcd".HexToBytes()); + mpt.TryPut("0a0c0909".HexToBytes(), "2222".HexToBytes()); + mpt.TryPut("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); } @@ -154,9 +153,42 @@ public void TestTryDelete() [TestMethod] public void TestGetProof() { + var r = new ShortNode(); + r.Key = "0a0c".HexToBytes(); + var b = new FullNode(); + var l1 = new ShortNode(); + l1.Key = new byte[]{0x01}; + var l2 = new ShortNode(); + l2.Key = new byte[]{0x09}; + var v1 = new ValueNode(); + v1.Value = "abcd".HexToBytes(); + var v2 = new ValueNode(); + v2.Value = "2222".HexToBytes(); + var h1 = new HashNode(); + h1.Hash = Encoding.ASCII.GetBytes("hello"); + var l3 = new ShortNode(); + l3.Next = h1; + l3.Key = "0e".HexToBytes(); + + r.Next = b; + b.Children[0] = l1; + l1.Next = v1; + b.Children[9] = l2; + l2.Next = v2; + b.Children[10] = l3; + var store = new MemoryStore(); var mptdb = new MPTDatabase(store); - var mpt = new MPTTrie(mptdb, new byte[]{}); + var mpt = new MPTTrie(mptdb, r); + + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); + var dict = mpt.GetProof("0a0c0001".HexToBytes()); + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); + Assert.AreEqual(4, dict.Count); + Assert.IsTrue(dict.TryGetValue(mpt.GetRoot(), out byte[] value)); + Assert.IsTrue(dict.TryGetValue(b.GetHash(), out value)); + Assert.IsTrue(dict.TryGetValue(l1.GetHash(), out value)); + Assert.IsTrue(dict.TryGetValue(v1.GetHash(), out value)); } } } \ No newline at end of file From 468b8825db5d8a3a04a56837734daa100b7d734b Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 15:25:31 +0800 Subject: [PATCH 005/171] add ut --- tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 26 ++++++++++++++++++++++ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 3 +-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs index 1cf88b8bf9..5a2dc9cf1d 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -42,5 +42,31 @@ public void TestSkip() s = s.Skip(2); Assert.AreEqual(0, s.Length); } + + [TestMethod] + public void TestCommonPrefix() + { + var a = "1234abcd".HexToBytes(); + var b = "".HexToBytes(); + var prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + + b = "100000".HexToBytes(); + prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + + b = "1234".HexToBytes(); + prefix = a.CommonPrefix(b); + Assert.AreEqual("1234", prefix.ToHexString()); + + b = a; + prefix = a.CommonPrefix(b); + Assert.AreEqual("1234abcd", prefix.ToHexString()); + + a = new byte[0]; + b = new byte[0]; + prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + } } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 6c8f35f868..bbb455c59e 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -80,8 +80,8 @@ public void TestTryGetResolve() var n = new ValueNode(); n.Value = Encoding.ASCII.GetBytes("hello"); var store = new MemoryStore(); - store.Put(MPTDatabase.TABLE, n.GetHash(), n.Encode()); var mptdb = new MPTDatabase(store); + mptdb.Put(n); var mpt = new MPTTrie(mptdb, root); var result = mpt.TryGet("0a0c0a0e".HexToBytes(), out byte[] value); @@ -183,7 +183,6 @@ public void TestGetProof() Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); var dict = mpt.GetProof("0a0c0001".HexToBytes()); - Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); Assert.AreEqual(4, dict.Count); Assert.IsTrue(dict.TryGetValue(mpt.GetRoot(), out byte[] value)); Assert.IsTrue(dict.TryGetValue(b.GetHash(), out value)); From 9dca50c65a4670ca8da0833a4dbdef45ef1982e7 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 15:41:54 +0800 Subject: [PATCH 006/171] format --- src/neo/Trie/MPT/Helper.cs | 7 ++++--- src/neo/Trie/MPT/MPTTrie.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs index acab94f28c..86f6175287 100644 --- a/src/neo/Trie/MPT/Helper.cs +++ b/src/neo/Trie/MPT/Helper.cs @@ -14,7 +14,7 @@ public static byte[] Concat(this byte[] a, byte[] b) public static byte[] CommonPrefix(this byte[] a, byte[] b) { - var prefix = new byte[]{}; + var prefix = new byte[] { }; var minLen = a.Length <= b.Length ? a.Length : b.Length; if (a.Length != 0 && b.Length != 0) @@ -40,9 +40,10 @@ public static bool Equal(this byte[] a, byte[] b) public static byte[] Skip(this byte[] a, int count) { - var result = new byte[]{}; + var result = new byte[] { }; var len = a.Length - count; - if (0 < len) { + if (0 < len) + { result = new byte[len]; Array.Copy(a, count, result, 0, len); } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 8c42ebd631..cd821b1d55 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -93,7 +93,7 @@ public bool TryPut(byte[] path, byte[] value) { var n = new ValueNode(value); path = (byte[])path.Clone(); - if (0 == value.Length) + if (value.Length == 0) { return tryDelete(ref root, path); } From d4075322f504973b922ae3295e1edd1746826a4b Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 16:17:42 +0800 Subject: [PATCH 007/171] fix some --- src/neo/Trie/MPT/MPTTrie.cs | 94 ++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index cd821b1d55..9872d38864 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -102,42 +102,44 @@ public bool TryPut(byte[] path, byte[] value) private bool put(ref MPTNode node, byte[] path, MPTNode val) { - var result = false; - var oldHash = node.GetHash(); switch (node) { case ValueNode valueNode: { if (path.Length == 0 && val is ValueNode vn) { + db.Delete(node.GetHash()); node = val; - node.ResetFlag(); db.Put(node); - result = true; - break; + return true; } - break; + return false; } case ShortNode shortNode: { var prefix = shortNode.Key.CommonPrefix(path); + var oldHash = shortNode.GetHash(); if (prefix.Length == shortNode.Key.Length) { - result = put(ref shortNode.Next, path.Skip(prefix.Length), val); + var result = put(ref shortNode.Next, path.Skip(prefix.Length), val); if (result) { + db.Delete(oldHash); shortNode.ResetFlag(); db.Put(shortNode); } - break; + return result; } var pathRemain = path.Skip(prefix.Length); var keyRemain = shortNode.Key.Skip(prefix.Length); var son = new FullNode(); MPTNode grandSon1 = HashNode.EmptyNode(), grandSon2 = HashNode.EmptyNode(); + put(ref grandSon1, keyRemain.Skip(1), shortNode.Next); + db.Put(grandSon1); son.Children[keyRemain[0]] = grandSon1; + if (pathRemain.Length == 0) { put(ref grandSon2, pathRemain, val); @@ -148,6 +150,7 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) put(ref grandSon2, pathRemain.Skip(1), val); son.Children[pathRemain[0]] = grandSon2; } + db.Put(grandSon2); db.Put(son); if (0 < prefix.Length) { @@ -156,18 +159,20 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) Key = prefix, Next = son, }; + db.Put(extensionNode); node = extensionNode; - db.Put(node); } else { node = son; } - result = true; - break; + db.Delete(oldHash); + return true; } case FullNode fullNode: { + var result = false; + var oldHash = fullNode.GetHash(); if (path.Length == 0) { result = put(ref fullNode.Children[fullNode.Children.Length], path, val); @@ -178,10 +183,11 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) } if (result) { + db.Delete(oldHash); fullNode.ResetFlag(); db.Put(fullNode); } - break; + return result; } case HashNode hashNode: { @@ -194,18 +200,14 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) }; node = newNode; db.Put(node); - result = true; - break; + return true; } node = Resolve(hashNode.Hash); - result = put(ref node, path, val); - break; + return put(ref node, path, val); } default: throw new System.Exception(); } - if (result) db.Delete(oldHash); - return result; } public bool TryDelete(byte[] path) @@ -215,47 +217,51 @@ public bool TryDelete(byte[] path) private bool tryDelete(ref MPTNode node, byte[] path) { - var result = false; - var oldHash = node.GetHash(); - switch (node) { case ValueNode valueNode: { if (path.Length == 0) { + db.Delete(valueNode.GetHash()); node = HashNode.EmptyNode(); - result = true; + return true; } - break; + return false; } case ShortNode shortNode: { var prefix = shortNode.Key.CommonPrefix(path); + var oldHash = shortNode.GetHash(); if (prefix.Length == shortNode.Key.Length) { - result = tryDelete(ref shortNode.Next, path.Skip(prefix.Length)); - if (!result) break; + var result = tryDelete(ref shortNode.Next, path.Skip(prefix.Length)); + if (!result) return false; + db.Delete(oldHash); if (shortNode.Next is HashNode hashNode && hashNode.IsEmptyNode) { node = shortNode.Next; - db.Put(node); } - if (shortNode.Next is ShortNode sn) + else if (shortNode.Next is ShortNode sn) { shortNode.Key = shortNode.Key.Concat(sn.Key); shortNode.Next = sn.Next; shortNode.ResetFlag(); db.Put(shortNode); } - result = true; - break; + else + { + node.ResetFlag(); + db.Put(shortNode); + } + return true; } - result = false; - break; + return false; } case FullNode fullNode: { + var result = false; + var oldHash = fullNode.GetHash(); if (path.Length == 0) { result = tryDelete(ref fullNode.Children[fullNode.Children.Length], path); @@ -264,7 +270,8 @@ private bool tryDelete(ref MPTNode node, byte[] path) { result = tryDelete(ref fullNode.Children[path[0]], path.Skip(1)); } - if (!result) break; + if (!result) return false; + db.Delete(oldHash); var nonEmptyChildren = new byte[] { }; for (int i = 0; i < fullNode.Children.Length; i++) { @@ -275,18 +282,20 @@ private bool tryDelete(ref MPTNode node, byte[] path) { fullNode.ResetFlag(); db.Put(fullNode); - break; + return true; } var childIndex = nonEmptyChildren[0]; var child = fullNode.Children[childIndex]; - if (child is HashNode hashNode) child = Resolve(hashNode.Hash); + if (child is HashNode hashNode) + child = Resolve(hashNode.Hash); if (child is ShortNode shortNode) { db.Delete(shortNode.GetHash()); shortNode.Key = nonEmptyChildren.Concat(shortNode.Key); + shortNode.ResetFlag(); db.Put(shortNode); - node = child; - break; + node = shortNode; + return true; } var newNode = new ShortNode() { @@ -295,23 +304,20 @@ private bool tryDelete(ref MPTNode node, byte[] path) }; node = newNode; db.Put(node); - result = true; - break; + return true; } case HashNode hashNode: { if (hashNode.IsEmptyNode) { - result = false; - break; + return true; } node = Resolve(hashNode.Hash); - result = tryDelete(ref node, path); - break; + return tryDelete(ref node, path); } + default: + return false; } - if (result) db.Delete(oldHash); - return result; } public byte[] GetRoot() From 61467d79decc6b270f5e8651d4371dc3168c0249 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 17 Jan 2020 15:54:39 +0800 Subject: [PATCH 008/171] fix interface --- src/neo/Trie/MPT/Helper.cs | 11 ++++++ src/neo/Trie/MPT/MPTDatabase.cs | 20 +++++++++-- src/neo/Trie/MPT/MPTTrie.cs | 34 +++++++++---------- src/neo/Trie/Trie.cs | 2 ++ tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 8 +++++ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 39 ++++++++++++---------- 6 files changed, 75 insertions(+), 39 deletions(-) diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs index 86f6175287..6b6a4c0445 100644 --- a/src/neo/Trie/MPT/Helper.cs +++ b/src/neo/Trie/MPT/Helper.cs @@ -57,5 +57,16 @@ public static byte[] Add(this byte[] a, byte b) result[a.Length] = b; return result; } + + public static byte[] ToNibbles(this byte[] path) + { + var result = new byte[path.Length * 2]; + for (int i = 0; i < path.Length; i++) + { + result[i * 2] = (byte)(path[i] >> 4); + result[i * 2 + 1] = (byte)(path[i] & 0x0F); + } + return result; + } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index cf9efcb00b..0563721ead 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,12 +1,13 @@ using Neo.Persistence; using System.Text; +using System; namespace Neo.Trie.MPT { public class MPTDatabase { - private IStore store; + private ISnapshot store; public static readonly byte TABLE = 0x4D; @@ -17,7 +18,7 @@ private byte[] StoreKey(byte[] hash) return Prefix.Concat(hash); } - public MPTDatabase(IStore store) + public MPTDatabase(ISnapshot store) { this.store = store; } @@ -38,5 +39,20 @@ public void Put(MPTNode node) { store.Put(TABLE, StoreKey(node.GetHash()), node.Encode()); } + + public void PutRoot(byte[] root) + { + store.Put(TABLE, StoreKey(Encoding.ASCII.GetBytes("mpt_root")), root); + } + + public byte[] GetRoot() + { + return store.TryGet(TABLE, StoreKey(Encoding.ASCII.GetBytes("mpt_root"))); + } + + public void Commit() + { + store.Commit(); + } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 9872d38864..6828ba9ee1 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,4 +1,5 @@ using Neo.IO.Json; +using Neo.Persistence; using System.Collections.Generic; namespace Neo.Trie.MPT @@ -8,33 +9,19 @@ public class MPTTrie : ITrie private MPTDatabase db; private MPTNode root; - public MPTTrie(MPTDatabase db, byte[] root) + public MPTTrie(MPTDatabase db) { if (db is null) throw new System.Exception(); this.db = db; - if (root.Length == 0) + var rbytes = db.GetRoot(); + if (rbytes.Length == 0) { this.root = HashNode.EmptyNode(); } else { - this.root = Resolve(root); - } - } - - public MPTTrie(MPTDatabase db, MPTNode root) - { - if (db is null) - throw new System.Exception(); - this.db = db; - if (root is null) - { - this.root = HashNode.EmptyNode(); - } - else - { - this.root = root; + this.root = Resolve(rbytes); } } @@ -45,6 +32,7 @@ public MPTNode Resolve(byte[] hash) public bool TryGet(byte[] path, out byte[] value) { + path = path.ToNibbles(); return tryGet(ref root, path, out value); } @@ -92,7 +80,7 @@ private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) public bool TryPut(byte[] path, byte[] value) { var n = new ValueNode(value); - path = (byte[])path.Clone(); + path = path.ToNibbles(); if (value.Length == 0) { return tryDelete(ref root, path); @@ -212,6 +200,7 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) public bool TryDelete(byte[] path) { + path = path.ToNibbles(); return tryDelete(ref root, path); } @@ -328,6 +317,7 @@ public byte[] GetRoot() public Dictionary GetProof(byte[] path) { var dict = new Dictionary { }; + path = path.ToNibbles(); getProof(ref root, path, dict); return dict; } @@ -378,6 +368,12 @@ private void getProof(ref MPTNode node, byte[] path, Dictionary } } + public void Commit() + { + db.PutRoot(GetRoot()); + db.Commit(); + } + public JObject ToJson() { return root.ToJson(); diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index 37c059b245..decd4ac912 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -14,5 +14,7 @@ public interface ITrie byte[] GetRoot(); Dictionary GetProof(byte[] Key); + + void Commit(); } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs index 5a2dc9cf1d..a826bf04d8 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -68,5 +68,13 @@ public void TestCommonPrefix() prefix = a.CommonPrefix(b); Assert.IsTrue(prefix.Length == 0); } + + [TestMethod] + public void TestToNibbles() + { + var a = "1234abcd".HexToBytes(); + var n = a.ToNibbles(); + Assert.AreEqual("010203040a0b0c0d", n.ToHexString()); + } } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index bbb455c59e..9c0c11a298 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -12,6 +12,8 @@ public class UT_MPTTrie { private MPTNode root; + private MPTDatabase mptdb; + [ClassInitialize] public static void ClassInit(TestContext context) { @@ -45,14 +47,23 @@ public void TestInit() l2.Next = v2; b.Children[10] = l3; root = r; + var store = new MemoryStore(); + this.mptdb = new MPTDatabase(store.GetSnapshot()); + mptdb.PutRoot(root.GetHash()); + mptdb.Put(r); + mptdb.Put(b); + mptdb.Put(l1); + mptdb.Put(l2); + mptdb.Put(l3); + mptdb.Put(v1); + mptdb.Put(v2); } [TestMethod] public void TestTryGet() { - var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); - var mpt = new MPTTrie(mptdb, root); + + var mpt = new MPTTrie(mptdb); var result = mpt.TryGet("0a0c0001".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.AreEqual("abcd", value.ToHexString()); @@ -79,10 +90,8 @@ public void TestTryGetResolve() { var n = new ValueNode(); n.Value = Encoding.ASCII.GetBytes("hello"); - var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); mptdb.Put(n); - var mpt = new MPTTrie(mptdb, root); + var mpt = new MPTTrie(mptdb); var result = mpt.TryGet("0a0c0a0e".HexToBytes(), out byte[] value); Assert.IsTrue(result); @@ -93,11 +102,10 @@ public void TestTryGetResolve() public void TestTryPut() { var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); - var sn = (ShortNode)root; - Assert.IsTrue(root is ShortNode); - Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", sn.GetHash().ToHexString()); - var mpt = new MPTTrie(mptdb, new byte[]{}); + var db = new MPTDatabase(store.GetSnapshot()); + var mpt1 = new MPTTrie(mptdb); + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt1.GetRoot().ToHexString()); + var mpt = new MPTTrie(db); mpt.TryPut("0a0c0001".HexToBytes(), "abcd".HexToBytes()); mpt.TryPut("0a0c0909".HexToBytes(), "2222".HexToBytes()); mpt.TryPut("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); @@ -107,9 +115,6 @@ public void TestTryPut() [TestMethod] public void TestTryDelete() { - var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); - var r1 = new ShortNode(); r1.Key = "0a0c0001".HexToBytes(); @@ -139,7 +144,7 @@ public void TestTryDelete() Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", r1.GetHash().ToHexString()); Assert.AreEqual("f3ad94e8fb6e1e85a8b573b2343845e3b0e0b96b61fcd0e20b6df159fde137a7", r.GetHash().ToHexString()); - var mpt = new MPTTrie(mptdb, r); + var mpt = new MPTTrie(mptdb); var result = true; result = mpt.TryGet("0a0c0909".HexToBytes(), out byte[] value); Assert.IsTrue(result); @@ -177,9 +182,7 @@ public void TestGetProof() l2.Next = v2; b.Children[10] = l3; - var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); - var mpt = new MPTTrie(mptdb, r); + var mpt = new MPTTrie(mptdb); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); var dict = mpt.GetProof("0a0c0001".HexToBytes()); From 8605d632de761ddd71c4981b5b8d90027b855271 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 17 Jan 2020 16:40:21 +0800 Subject: [PATCH 009/171] format --- src/neo/Trie/MPT/MPTDatabase.cs | 1 - src/neo/Trie/MPT/MPTNode.cs | 5 +---- src/neo/Trie/MPT/MPTNode/FullNode.cs | 13 ------------- src/neo/Trie/MPT/MPTNode/HashNode.cs | 19 +++---------------- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 15 ++++----------- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 8 -------- src/neo/Trie/MPT/MPTTrie.cs | 9 +-------- 7 files changed, 9 insertions(+), 61 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 0563721ead..478a6f4e11 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,7 +1,6 @@ using Neo.Persistence; using System.Text; -using System; namespace Neo.Trie.MPT { diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 84f62b2107..e695b82cb6 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -2,7 +2,6 @@ using System.Text; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -60,7 +59,7 @@ public virtual void Serialize(BinaryWriter writer) public virtual void Deserialize(BinaryReader reader) { - + } public byte[] Encode() @@ -101,7 +100,5 @@ public static MPTNode Decode(byte[] data) } } } - - public abstract JObject ToJson(); } } diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 9dba385192..76cf7ddedc 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -1,7 +1,6 @@ using System.IO; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -57,17 +56,5 @@ public override void Deserialize(BinaryReader reader) Children[i] = hashNode; } } - - public override JObject ToJson() - { - var json = new JObject(); - var jchildren = new JArray(); - for (int i = 0; i < Children.Length; i++) - { - jchildren.Add(Children[i].ToJson()); - } - json["children"] = jchildren; - return json; - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index 057ffa25f6..6837763a9e 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -1,6 +1,3 @@ -using System.IO; -using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -11,14 +8,14 @@ public class HashNode : MPTNode public HashNode() { nType = NodeType.HashNode; - } + } public HashNode(byte[] hash) { nType = NodeType.HashNode; Hash = (byte[])hash.Clone(); } - + protected override byte[] calHash() { return (byte[])Hash.Clone(); @@ -26,19 +23,9 @@ protected override byte[] calHash() public static HashNode EmptyNode() { - return new HashNode(new byte[]{}); + return new HashNode(new byte[] { }); } public bool IsEmptyNode => Hash.Length == 0; - - public override JObject ToJson() - { - var json = new JObject(); - if (!this.IsEmptyNode) - { - json["hash"] = Hash.ToHexString(); - } - return json; - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 5564e2bbcf..891b450095 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -1,7 +1,6 @@ using System.IO; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -13,7 +12,8 @@ public class ShortNode : MPTNode public new int Size => Key.Length + Next.Size; - protected override byte[] calHash(){ + protected override byte[] calHash() + { return Key.Concat(Next.GetHash()).Sha256(); } public ShortNode() @@ -23,7 +23,8 @@ public ShortNode() public ShortNode Clone() { - var cloned = new ShortNode() { + var cloned = new ShortNode() + { Key = (byte[])Key.Clone(), Next = Next, }; @@ -43,13 +44,5 @@ public override void Deserialize(BinaryReader reader) var hashNode = new HashNode(reader.ReadVarBytes()); Next = hashNode; } - - public override JObject ToJson() - { - var json = new JObject(); - json["key"] = Key.ToHexString(); - json["next"] = Next.ToJson(); - return json; - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index b4c84cbe04..e300620c2d 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -1,7 +1,6 @@ using System.IO; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -35,12 +34,5 @@ public override void Deserialize(BinaryReader reader) { Value = reader.ReadVarBytes(); } - - public override JObject ToJson() - { - var json = new JObject(); - json["value"] = Value.ToHexString(); - return json; - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 6828ba9ee1..a82e364973 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,5 +1,3 @@ -using Neo.IO.Json; -using Neo.Persistence; using System.Collections.Generic; namespace Neo.Trie.MPT @@ -275,7 +273,7 @@ private bool tryDelete(ref MPTNode node, byte[] path) } var childIndex = nonEmptyChildren[0]; var child = fullNode.Children[childIndex]; - if (child is HashNode hashNode) + if (child is HashNode hashNode) child = Resolve(hashNode.Hash); if (child is ShortNode shortNode) { @@ -373,10 +371,5 @@ public void Commit() db.PutRoot(GetRoot()); db.Commit(); } - - public JObject ToJson() - { - return root.ToJson(); - } } } \ No newline at end of file From f45396ccbf11b6b5910a27aff80680068cb97415 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 17 Jan 2020 16:41:23 +0800 Subject: [PATCH 010/171] rm nullnode typee --- src/neo/Trie/MPT/MPTNode.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index e695b82cb6..505a15926d 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -11,7 +11,6 @@ public enum NodeType ShortNode, HashNode, ValueNode, - NullNode = 0xFF } public class NodeFlag From 8f301d9d16717cc62116b8a9b884247ed49eadfe Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 10 Feb 2020 10:50:33 +0800 Subject: [PATCH 011/171] rm fullnode size --- src/neo/Trie/MPT/MPTNode/FullNode.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 76cf7ddedc..ff3f493d2a 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -8,8 +8,6 @@ public class FullNode : MPTNode { public MPTNode[] Children = new MPTNode[17]; - public new int Size; - public FullNode() { nType = NodeType.FullNode; From 3d01c5be346549ef10a1450b26a0376bdb411c0e Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 10 Feb 2020 14:22:07 +0800 Subject: [PATCH 012/171] rm Database commit --- src/neo/Trie/MPT/MPTDatabase.cs | 5 ----- src/neo/Trie/MPT/MPTTrie.cs | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 478a6f4e11..51b09ed9c5 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -48,10 +48,5 @@ public byte[] GetRoot() { return store.TryGet(TABLE, StoreKey(Encoding.ASCII.GetBytes("mpt_root"))); } - - public void Commit() - { - store.Commit(); - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index a82e364973..019495105b 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -13,7 +13,7 @@ public MPTTrie(MPTDatabase db) throw new System.Exception(); this.db = db; var rbytes = db.GetRoot(); - if (rbytes.Length == 0) + if (rbytes is null || rbytes.Length == 0) { this.root = HashNode.EmptyNode(); } @@ -369,7 +369,6 @@ private void getProof(ref MPTNode node, byte[] path, Dictionary public void Commit() { db.PutRoot(GetRoot()); - db.Commit(); } } } \ No newline at end of file From eb3c77708172288cc4be6bb2587bda18ecab83d2 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 10 Jan 2020 17:52:54 +0800 Subject: [PATCH 013/171] first push --- src/neo/Trie/Database.cs | 8 ++ src/neo/Trie/MPT/Helper.cs | 59 ++++++++ src/neo/Trie/MPT/MPTDatabase.cs | 29 ++++ src/neo/Trie/MPT/MPTNode.cs | 155 +++++++++++++++++++++ src/neo/Trie/MPT/MPTTrie.cs | 104 ++++++++++++++ src/neo/Trie/Trie.cs | 8 ++ tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 26 ++++ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 56 ++++++++ tests/neo.UnitTests/Trie/MPT/UT_Node.cs | 0 9 files changed, 445 insertions(+) create mode 100644 src/neo/Trie/Database.cs create mode 100644 src/neo/Trie/MPT/Helper.cs create mode 100644 src/neo/Trie/MPT/MPTDatabase.cs create mode 100644 src/neo/Trie/MPT/MPTNode.cs create mode 100644 src/neo/Trie/MPT/MPTTrie.cs create mode 100644 src/neo/Trie/Trie.cs create mode 100644 tests/neo.UnitTests/Trie/MPT/UT_Helper.cs create mode 100644 tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs create mode 100644 tests/neo.UnitTests/Trie/MPT/UT_Node.cs diff --git a/src/neo/Trie/Database.cs b/src/neo/Trie/Database.cs new file mode 100644 index 0000000000..ff16c9e0de --- /dev/null +++ b/src/neo/Trie/Database.cs @@ -0,0 +1,8 @@ + +namespace Neo.Trie +{ + public abstract class Database + { + + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs new file mode 100644 index 0000000000..bf15117ef6 --- /dev/null +++ b/src/neo/Trie/MPT/Helper.cs @@ -0,0 +1,59 @@ +using System; + +namespace Neo.Trie.MPT +{ + public static class Helper + { + public static byte[] Concat(this byte[] a, byte[] b) + { + var result = new byte[a.Length + b.Length]; + a.CopyTo(result, 0); + b.CopyTo(result, a.Length); + return result; + } + + public static byte[] CommonPrefix(this byte[] a, byte[] b) + { + var prefix = new byte[]{}; + var minLen = a.Length <= b.Length ? a.Length : b.Length; + + if (a.Length == 0 || b.Length == 0 || a[0] != b[0]) return prefix; + + for (int i = 0; i < minLen; i++) + { + if (a[i] != b[i]) break; + prefix = prefix.Add(a[i]); + } + return prefix; + } + + public static bool Equal(this byte[] a, byte[] b) + { + if (a.Length != b.Length) return false; + for (int i = 0; i < a.Length; i++) + { + if (a[i] != b[i]) return false; + } + return true; + } + + public static byte[] Skip(this byte[] a, int count) + { + var result = new byte[]{}; + var len = a.Length - count; + if (0 < len) { + result = new byte[len]; + Array.Copy(a, count, result, 0, len); + } + return result; + } + + public static byte[] Add(this byte[] a, byte b) + { + var result = new byte[a.Length + 1]; + a.CopyTo(result, 0); + result[a.Length] = b; + return result; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs new file mode 100644 index 0000000000..8aa5004f25 --- /dev/null +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -0,0 +1,29 @@ + +using Neo.Persistence; + +namespace Neo.Trie.MPT +{ + public class MPTDatabase: Database + { + private IStore store; + + private byte TABLE = 0x54; + + private byte[] GetStoreKey(byte[] hash) + { + return hash; + } + + public MPTDatabase(IStore store) + { + this.store = store; + } + + public MPTNode Node(byte[] hash) + { + var data = store.TryGet(TABLE, GetStoreKey(hash)); + var n = MPTNode.Decode(data); + return n; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs new file mode 100644 index 0000000000..34a35fa48e --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -0,0 +1,155 @@ +using System.IO; +using Neo.IO; +using Neo.Cryptography; + +namespace Neo.Trie.MPT +{ + enum NodeType + { + BranchNode, + ExtensionNode, + LeafNode, + HashNode, + ValueNode + } + public class NodeFlag + { + public byte[] Hash; + public bool dirty; + } + public abstract class MPTNode: ISerializable + { + + public NodeFlag Flag { get; } + + public int Size { get; } + + public static MPTNode Decode(byte[] data) + { + var nodeType = (NodeType)data[0]; + data = data.Skip(1); + switch (nodeType) + { + case NodeType.BranchNode: + return BranchNode.Decode(data); + case NodeType.ExtensionNode: + return ExtensionNode.Decode(data); + case NodeType.LeafNode: + return LeafNode.Decode(data); + case NodeType.ValueNode: + return ValueNode.Decode(data); + default: + throw new System.Exception(); + } + } + + } + + public class ExtensionNode : MPTNode + { + public byte[] Key; + public MPTNode Next; + + public ExtensionNode Clone() + { + var cloned = new ExtensionNode(); + cloned.Key = (byte[])Key.Clone(); + cloned.Next = Next; + return cloned; + } + + public byte[] Encode() + { + return new byte[]{}; + } + + public new static ExtensionNode Decode(byte[] data) + { + var n = new ExtensionNode(); + return n; + } + } + + public class BranchNode : MPTNode + { + public MPTNode[] Children = new MPTNode[17]; + + public BranchNode Clone() + { + var cloned = new BranchNode(); + for (int i = 0; i < Children.Length; i++) + { + cloned.Children[i] = Children[i]; + } + return cloned; + } + + public override void Serialize(BinaryWriter writer) + { + writer.WriteNullableArray(Children); + } + + public override void Deserialize(BinaryReader reader) + { + + } + } + + public class LeafNode : MPTNode + { + public byte[] Key; + public MPTNode Value; + + public override void Serialize(BinaryWriter writer) + { + + } + + public override void Deserialize(BinaryReader reader) + { + Value = new HashNode(reader.ReadVarBytes()); + } + } + + public class HashNode : MPTNode + { + public byte[] Hash; + + public HashNode(byte[] hash) + { + Hash = new byte[hash.Length]; + hash.CopyTo(Hash, 0); + } + + public override void Serialize(BinaryWriter writer) + { + writer.WriteVarBytes(Hash); + } + + public override void Deserialize(BinaryReader reader) + { + Hash = reader.ReadVarBytes(); + } + } + + public class ValueNode : MPTNode + { + public byte[] Value; + + public ValueNode(byte[] val) + { + Value = new byte[val.Length]; + val.CopyTo(Value, 0); + } + + public override void Serialize(BinaryWriter writer) + { + writer.WriteVarBytes(Value); + } + + public override void Deserialize(BinaryReader reader) + { + Value = reader.ReadVarBytes(); + } + } +} diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs new file mode 100644 index 0000000000..454d68d518 --- /dev/null +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -0,0 +1,104 @@ + +using System; + +namespace Neo.Trie.MPT +{ + public class MPTTrie: Trie + { + private MPTDatabase db; + private MPTNode root; + public MPTTrie(MPTDatabase db, byte[] root) + { + + } + + public MPTTrie(MPTDatabase db, MPTNode root) + { + this.db = db; + this.root = root; + } + + public byte[] GetRoot => Hash(); + + public MPTNode Resolve(byte[] hash) + { + return db.Node(hash); + } + + public byte[] Hash() + { + return new byte[]{}; + } + + public bool TryGet(byte[] path, out byte[] value) + { + return tryGet(ref root, path, out value); + } + + private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) + { + switch(node) + { + case ValueNode valueNode: + { + value = (byte[])valueNode.Value.Clone(); + return true; + } + case HashNode hashNode: + { + var result = false; + node = Resolve(hashNode.Hash); + result = tryGet(ref node, path, out value); + return result; + } + case BranchNode branchNode: + { + if (0 == path.Length) { + return tryGet(ref branchNode.Children[16], path, out value); + } + return tryGet(ref branchNode.Children[path[0]], path.Skip(1), out value); + } + case ExtensionNode extensionNode: + { + var prefix = extensionNode.Key.CommonPrefix(path); + if (prefix.Length == extensionNode.Key.Length) + { + return tryGet(ref extensionNode.Next, path.Skip(prefix.Length), out value); + } + break; + } + case LeafNode leafNode: + { + if (leafNode.Key.Equal(path)) + { + return tryGet(ref leafNode.Value, path, out value); + } + break; + } + } + value = new byte[]{}; + return false; + } + + public bool TryUpdate(byte[] path, byte[] value) + { + return true; + } + + private bool tryUpdate(byte[] path, byte[] value) + { + return true; + } + + public bool TryDelete(byte[] path) + { + return true; + } + + private bool tryDelete(byte[] path, out MPTNode newNode) + { + newNode = null; + return true; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs new file mode 100644 index 0000000000..68b4217972 --- /dev/null +++ b/src/neo/Trie/Trie.cs @@ -0,0 +1,8 @@ + +namespace Neo.Trie +{ + public abstract class Trie + { + + } +} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs new file mode 100644 index 0000000000..a76feb8e57 --- /dev/null +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -0,0 +1,26 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Trie.MPT; + +namespace Neo.UnitTests.Trie.MPT +{ + [TestClass] + public class UT_Helper + { + [TestMethod] + public void TestAdd() + { + var a = "ab".HexToBytes(); + byte b = 0x0c; + a.Add(b); + Assert.AreEqual("ab", a.ToHexString()); + } + + [TestMethod] + public void TestSkip() + { + var s = "abcd01".HexToBytes(); + s = s.Skip(2); + Assert.AreEqual("01", s.ToHexString()); + } + } +} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs new file mode 100644 index 0000000000..63754df43b --- /dev/null +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -0,0 +1,56 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Trie.MPT; + +namespace Neo.UnitTests.Trie.MPT +{ + [TestClass] + public class UT_MPTTrie + { + private static Node root; + + [ClassInitialize] + public static void ClassInit(TestContext context) + { + var r = new ExtensionNode(); + var b = new BranchNode(); + var l1 = new LeafNode(); + var l2 = new LeafNode(); + var v1 = new ValueNode("abcd".HexToBytes()); + var v2 = new ValueNode("2222".HexToBytes()); + r.Key = "0a0c".HexToBytes(); + l1.Key = new byte[]{0x01}; + l2.Key = new byte[]{0x09}; + r.Next = b; + b.Children[0] = l1; + l1.Value = v1; + b.Children[9] = l2; + l2.Value = v2; + root = r; + } + + [TestMethod] + public void TestTryGet() + { + var mpt = new MPTTrie(null, root); + var result = mpt.TryGet("0a0c0001".HexToBytes(), out byte[] value); + Assert.IsTrue(result); + Assert.AreEqual("abcd", value.ToHexString()); + + result = mpt.TryGet("0a0c0909".HexToBytes(), out value); + Assert.IsTrue(result); + Assert.AreEqual("2222", value.ToHexString()); + + result = mpt.TryGet("0a0b0909".HexToBytes(), out value); + Assert.IsFalse(result); + + result = mpt.TryGet("0a0c0309".HexToBytes(), out value); + Assert.IsFalse(result); + + result = mpt.TryGet("0a0c0002".HexToBytes(), out value); + Assert.IsFalse(result); + + result = mpt.TryGet("0a0c090901".HexToBytes(), out value); + Assert.IsFalse(result); + } + } +} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Node.cs b/tests/neo.UnitTests/Trie/MPT/UT_Node.cs new file mode 100644 index 0000000000..e69de29bb2 From 8c05f7651fd9a2cf45dec6268b58f8eb86fb35da Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 09:44:50 +0800 Subject: [PATCH 014/171] add TryDelete --- src/neo/Trie/MPT/Helper.cs | 11 +- src/neo/Trie/MPT/MPTDatabase.cs | 20 +- src/neo/Trie/MPT/MPTNode.cs | 252 +++++++++++++--- src/neo/Trie/MPT/MPTTrie.cs | 333 +++++++++++++++++---- src/neo/Trie/Trie.cs | 4 +- src/neo/Trie/{Database.cs => TrieDb.cs} | 2 +- tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 24 +- tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs | 27 ++ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 121 +++++++- tests/neo.UnitTests/Trie/MPT/UT_Node.cs | 0 10 files changed, 668 insertions(+), 126 deletions(-) rename src/neo/Trie/{Database.cs => TrieDb.cs} (55%) create mode 100644 tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs delete mode 100644 tests/neo.UnitTests/Trie/MPT/UT_Node.cs diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs index bf15117ef6..acab94f28c 100644 --- a/src/neo/Trie/MPT/Helper.cs +++ b/src/neo/Trie/MPT/Helper.cs @@ -17,12 +17,13 @@ public static byte[] CommonPrefix(this byte[] a, byte[] b) var prefix = new byte[]{}; var minLen = a.Length <= b.Length ? a.Length : b.Length; - if (a.Length == 0 || b.Length == 0 || a[0] != b[0]) return prefix; - - for (int i = 0; i < minLen; i++) + if (a.Length != 0 && b.Length != 0) { - if (a[i] != b[i]) break; - prefix = prefix.Add(a[i]); + for (int i = 0; i < minLen; i++) + { + if (a[i] != b[i]) break; + prefix = prefix.Add(a[i]); + } } return prefix; } diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 8aa5004f25..0601537db3 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,15 +1,17 @@ using Neo.Persistence; +using System.Threading; +using System; namespace Neo.Trie.MPT { - public class MPTDatabase: Database + public class MPTDatabase: ITrieDatabase { private IStore store; - private byte TABLE = 0x54; + public static readonly byte TABLE = 0x4D; - private byte[] GetStoreKey(byte[] hash) + private byte[] StoreKey(byte[] hash) { return hash; } @@ -21,9 +23,19 @@ public MPTDatabase(IStore store) public MPTNode Node(byte[] hash) { - var data = store.TryGet(TABLE, GetStoreKey(hash)); + var data = store.TryGet(TABLE, StoreKey(hash)); var n = MPTNode.Decode(data); return n; } + + public void Delete(byte[] hash) + { + store.Delete(TABLE, StoreKey(hash)); + } + + public void Put(MPTNode node) + { + store.Put(TABLE, StoreKey(node.GetHash()), node.Encode()); + } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 34a35fa48e..647fd1f07f 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -1,113 +1,221 @@ using System.IO; +using System.Text; using Neo.IO; +using Neo.IO.Json; using Neo.Cryptography; namespace Neo.Trie.MPT { - enum NodeType + public enum NodeType { - BranchNode, - ExtensionNode, - LeafNode, + FullNode, + ShortNode, HashNode, - ValueNode + ValueNode, + NullNode = 0xFF } + public class NodeFlag { public byte[] Hash; - public bool dirty; + public bool Dirty; + + public NodeFlag() + { + Dirty = true; + } } + public abstract class MPTNode: ISerializable { + public NodeFlag Flag; + protected NodeType nType; - public NodeFlag Flag { get; } + protected abstract byte[] calHash(); + + public virtual byte[] GetHash() { + if (!Flag.Dirty) return Flag.Hash; + Flag.Hash = calHash(); + Flag.Dirty = false; + return (byte[])Flag.Hash.Clone(); + } + + public void ResetFlag() + { + Flag = new NodeFlag(); + } public int Size { get; } + + public MPTNode() + { + Flag = new NodeFlag(); + } + + public virtual void Serialize(BinaryWriter writer) + { + writer.Write((byte)nType); + } + + public virtual void Deserialize(BinaryReader reader) + { + + } + + public byte[] Encode() + { + return this.ToArray(); + } public static MPTNode Decode(byte[] data) { var nodeType = (NodeType)data[0]; data = data.Skip(1); - switch (nodeType) + + using (MemoryStream ms = new MemoryStream(data, false)) + using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) { - case NodeType.BranchNode: - return BranchNode.Decode(data); - case NodeType.ExtensionNode: - return ExtensionNode.Decode(data); - case NodeType.LeafNode: - return LeafNode.Decode(data); - case NodeType.ValueNode: - return ValueNode.Decode(data); - default: - throw new System.Exception(); + switch (nodeType) + { + case NodeType.FullNode: + { + var n = new FullNode(); + n.Deserialize(reader); + return n; + } + case NodeType.ShortNode: + { + var n = new ShortNode(); + n.Deserialize(reader); + return n; + } + case NodeType.ValueNode: + { + var n = new ValueNode(); + n.Deserialize(reader); + return n; + } + default: + throw new System.Exception(); + } } } + public abstract JObject ToJson(); } - public class ExtensionNode : MPTNode + public class ShortNode : MPTNode { public byte[] Key; + public MPTNode Next; - public ExtensionNode Clone() + public new int Size => Key.Length + Next.Size; + + protected override byte[] calHash(){ + return Key.Concat(Next.GetHash()).Sha256(); + } + public ShortNode() + { + nType = NodeType.ShortNode; + } + + public ShortNode Clone() { - var cloned = new ExtensionNode(); - cloned.Key = (byte[])Key.Clone(); - cloned.Next = Next; + var cloned = new ShortNode() { + Key = (byte[])Key.Clone(), + Next = Next, + }; return cloned; } - public byte[] Encode() + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + writer.WriteVarBytes(Key); + writer.WriteVarBytes(Next.GetHash()); + } + + public override void Deserialize(BinaryReader reader) { - return new byte[]{}; + var hashNode = new HashNode(); + Key = reader.ReadVarBytes(); + hashNode.Deserialize(reader); + Next = hashNode; } - public new static ExtensionNode Decode(byte[] data) + public override JObject ToJson() { - var n = new ExtensionNode(); - return n; + var json = new JObject(); + json["key"] = Key.ToHexString(); + json["next"] = Next.ToJson(); + return json; } } - public class BranchNode : MPTNode + public class FullNode : MPTNode { public MPTNode[] Children = new MPTNode[17]; - public BranchNode Clone() + public new int Size; + + public FullNode() { - var cloned = new BranchNode(); + nType = NodeType.FullNode; for (int i = 0; i < Children.Length; i++) { - cloned.Children[i] = Children[i]; + Children[i] = HashNode.EmptyNode(); } - return cloned; } - public override void Serialize(BinaryWriter writer) + protected override byte[] calHash() { - writer.WriteNullableArray(Children); + var bytes = new byte[0]; + for (int i = 0; i < Children.Length; i++) + { + bytes = bytes.Concat(Children[i].GetHash()); + } + return bytes.Sha256(); } - public override void Deserialize(BinaryReader reader) + public FullNode Clone() { - + var cloned = new FullNode(); + for (int i = 0; i < Children.Length; i++) + { + cloned.Children[i] = Children[i]; + } + return cloned; } - } - - public class LeafNode : MPTNode - { - public byte[] Key; - public MPTNode Value; public override void Serialize(BinaryWriter writer) { - + base.Serialize(writer); + for (int i = 0; i < Children.Length; i++) + { + writer.WriteVarBytes(Children[i].GetHash()); + } } public override void Deserialize(BinaryReader reader) { - Value = new HashNode(reader.ReadVarBytes()); + for (int i = 0; i < Children.Length; i++) + { + var hashNode = new HashNode(reader.ReadVarBytes()); + Children[i] = hashNode; + } + } + + public override JObject ToJson() + { + var json = new JObject(); + var jchildren = new JArray(); + for (int i = 0; i < Children.Length; i++) + { + jchildren.Add(Children[i].ToJson()); + } + json["children"] = jchildren; + return json; } } @@ -115,14 +223,32 @@ public class HashNode : MPTNode { public byte[] Hash; + public HashNode() + { + nType = NodeType.HashNode; + } + public HashNode(byte[] hash) { - Hash = new byte[hash.Length]; - hash.CopyTo(Hash, 0); + nType = NodeType.HashNode; + Hash = (byte[])hash.Clone(); + } + + protected override byte[] calHash() + { + return (byte[])Hash.Clone(); + } + + public static HashNode EmptyNode() + { + return new HashNode(new byte[]{}); } + public bool IsEmptyNode => Hash.Length == 0; + public override void Serialize(BinaryWriter writer) { + base.Serialize(writer); writer.WriteVarBytes(Hash); } @@ -130,20 +256,41 @@ public override void Deserialize(BinaryReader reader) { Hash = reader.ReadVarBytes(); } + + public override JObject ToJson() + { + var json = new JObject(); + if (!this.IsEmptyNode) + { + json["hash"] = Hash.ToHexString(); + } + return json; + } } public class ValueNode : MPTNode { public byte[] Value; + protected override byte[] calHash() + { + return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); + } + + public ValueNode() + { + nType = NodeType.ValueNode; + } + public ValueNode(byte[] val) { - Value = new byte[val.Length]; - val.CopyTo(Value, 0); + nType = NodeType.ValueNode; + Value = (byte[])val.Clone(); } public override void Serialize(BinaryWriter writer) { + base.Serialize(writer); writer.WriteVarBytes(Value); } @@ -151,5 +298,12 @@ public override void Deserialize(BinaryReader reader) { Value = reader.ReadVarBytes(); } + + public override JObject ToJson() + { + var json = new JObject(); + json["value"] = Value.ToHexString(); + return json; + } } } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 454d68d518..c003c80f2b 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,104 +1,333 @@ - +using Neo.IO.Json; using System; namespace Neo.Trie.MPT { - public class MPTTrie: Trie + public class MPTTrie : ITrie { private MPTDatabase db; private MPTNode root; + public MPTTrie(MPTDatabase db, byte[] root) { - + if (db is null) + throw new System.Exception(); + this.db = db; + if (root.Length == 0) + { + this.root = HashNode.EmptyNode(); + } + else + { + this.root = Resolve(root); + } } - + public MPTTrie(MPTDatabase db, MPTNode root) { + if (db is null) + throw new System.Exception(); this.db = db; - this.root = root; + if (root is null) + { + this.root = HashNode.EmptyNode(); + } + else + { + this.root = root; + } } - public byte[] GetRoot => Hash(); - public MPTNode Resolve(byte[] hash) { return db.Node(hash); } - public byte[] Hash() - { - return new byte[]{}; - } - - public bool TryGet(byte[] path, out byte[] value) + public bool TryGet(byte[] path, out byte[] value) { return tryGet(ref root, path, out value); } private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) { - switch(node) + switch (node) { case ValueNode valueNode: - { - value = (byte[])valueNode.Value.Clone(); - return true; - } + { + if (path.Length == 0) + { + value = (byte[])valueNode.Value.Clone(); + return true; + } + break; + } case HashNode hashNode: - { - var result = false; - node = Resolve(hashNode.Hash); - result = tryGet(ref node, path, out value); - return result; - } - case BranchNode branchNode: - { - if (0 == path.Length) { - return tryGet(ref branchNode.Children[16], path, out value); + { + if (hashNode.IsEmptyNode) break; + node = Resolve(hashNode.Hash); + return tryGet(ref node, path, out value); } - return tryGet(ref branchNode.Children[path[0]], path.Skip(1), out value); - } - case ExtensionNode extensionNode: - { - var prefix = extensionNode.Key.CommonPrefix(path); - if (prefix.Length == extensionNode.Key.Length) + case FullNode fullNode: { - return tryGet(ref extensionNode.Next, path.Skip(prefix.Length), out value); + if (path.Length == 0) + { + return tryGet(ref fullNode.Children[16], path, out value); + } + return tryGet(ref fullNode.Children[path[0]], path.Skip(1), out value); } - break; - } - case LeafNode leafNode: - { - if (leafNode.Key.Equal(path)) + case ShortNode shortNode: { - return tryGet(ref leafNode.Value, path, out value); + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + return tryGet(ref shortNode.Next, path.Skip(prefix.Length), out value); + } + break; } - break; - } } - value = new byte[]{}; + value = new byte[] { }; return false; } - public bool TryUpdate(byte[] path, byte[] value) + public bool Put(byte[] path, byte[] value) { - return true; + var n = new ValueNode(value); + path = (byte[])path.Clone(); + if (0 == value.Length) + { + return tryDelete(ref root, path); + } + return put(ref root, path, n); } - private bool tryUpdate(byte[] path, byte[] value) + private bool put(ref MPTNode node, byte[] path, MPTNode val) { - return true; + var result = false; + var oldHash = node.GetHash(); + switch (node) + { + case ValueNode valueNode: + { + if (path.Length == 0 && val is ValueNode vn) + { + node = val; + node.ResetFlag(); + db.Put(node); + result = true; + break; + } + break; + } + case ShortNode shortNode: + { + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + result = put(ref shortNode.Next, path.Skip(prefix.Length), val); + if (result) + { + shortNode.ResetFlag(); + db.Put(shortNode); + } + break; + } + + var pathRemain = path.Skip(prefix.Length); + var keyRemain = shortNode.Key.Skip(prefix.Length); + var son = new FullNode(); + MPTNode grandSon1 = HashNode.EmptyNode(), grandSon2 = HashNode.EmptyNode(); + put(ref grandSon1, keyRemain.Skip(1), shortNode.Next); + son.Children[keyRemain[0]] = grandSon1; + if (pathRemain.Length == 0) + { + put(ref grandSon2, pathRemain, val); + son.Children[son.Children.Length] = grandSon2; + } + else + { + put(ref grandSon2, pathRemain.Skip(1), val); + son.Children[pathRemain[0]] = grandSon2; + } + db.Put(son); + if (0 < prefix.Length) + { + var extensionNode = new ShortNode() + { + Key = prefix, + Next = son, + }; + node = extensionNode; + db.Put(node); + } + else + { + node = son; + } + result = true; + break; + } + case FullNode fullNode: + { + if (path.Length == 0) + { + result = put(ref fullNode.Children[fullNode.Children.Length], path, val); + } + else + { + result = put(ref fullNode.Children[path[0]], path.Skip(1), val); + } + if (result) + { + fullNode.ResetFlag(); + db.Put(fullNode); + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) + { + var newNode = new ShortNode() + { + Key = path, + Next = val, + }; + node = newNode; + db.Put(node); + result = true; + break; + } + node = Resolve(hashNode.Hash); + result = put(ref node, path, val); + break; + } + default: + throw new System.Exception(); + } + if (result) db.Delete(oldHash); + return result; } public bool TryDelete(byte[] path) { - return true; + return tryDelete(ref root, path); + } + + private bool tryDelete(ref MPTNode node, byte[] path) + { + var result = false; + var oldHash = node.GetHash(); + + switch (node) + { + case ValueNode valueNode: + { + if (path.Length == 0) + { + node = HashNode.EmptyNode(); + result = true; + break; + } + break; + } + case ShortNode shortNode: + { + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + result = tryDelete(ref shortNode.Next, path.Skip(prefix.Length)); + if (!result) break; + if (shortNode.Next is HashNode hashNode && hashNode.IsEmptyNode) + { + node = shortNode.Next; + db.Put(node); + } + if (shortNode.Next is ShortNode sn) + { + shortNode.Key = shortNode.Key.Concat(sn.Key); + shortNode.Next = sn.Next; + shortNode.ResetFlag(); + db.Put(shortNode); + } + result = true; + break; + } + result = false; + break; + } + case FullNode fullNode: + { + if (path.Length == 0) + { + result = tryDelete(ref fullNode.Children[fullNode.Children.Length], path); + } + else + { + result = tryDelete(ref fullNode.Children[path[0]], path.Skip(1)); + } + if (!result) break; + var nonEmptyChildren = new byte[] { }; + for (int i = 0; i < fullNode.Children.Length; i++) + { + if (fullNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; + nonEmptyChildren = nonEmptyChildren.Add((byte)i); + } + if (1 < nonEmptyChildren.Length) + { + fullNode.ResetFlag(); + db.Put(fullNode); + break; + } + var childIndex = nonEmptyChildren[0]; + var child = fullNode.Children[childIndex]; + if (child is HashNode hashNode) child = Resolve(hashNode.Hash); + if (child is ShortNode shortNode) + { + db.Delete(shortNode.GetHash()); + shortNode.Key = nonEmptyChildren.Concat(shortNode.Key); + db.Put(shortNode); + node = child; + break; + } + var newNode = new ShortNode() + { + Key = nonEmptyChildren, + Next = child, + }; + node = newNode; + db.Put(node); + result = true; + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) + { + result = false; + break; + } + node = Resolve(hashNode.Hash); + result = tryDelete(ref node, path); + break; + } + } + if (result) db.Delete(oldHash); + return result; + } + + public byte[] GetRoot() + { + return this.root.GetHash(); + } + + public void Proof(byte[] path) + { + } - private bool tryDelete(byte[] path, out MPTNode newNode) + public JObject ToJson() { - newNode = null; - return true; + return root.ToJson(); } } } \ No newline at end of file diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index 68b4217972..4ee04fff7e 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -1,8 +1,8 @@ namespace Neo.Trie { - public abstract class Trie + public interface ITrie { - + } } \ No newline at end of file diff --git a/src/neo/Trie/Database.cs b/src/neo/Trie/TrieDb.cs similarity index 55% rename from src/neo/Trie/Database.cs rename to src/neo/Trie/TrieDb.cs index ff16c9e0de..06f798eeea 100644 --- a/src/neo/Trie/Database.cs +++ b/src/neo/Trie/TrieDb.cs @@ -1,7 +1,7 @@ namespace Neo.Trie { - public abstract class Database + public interface ITrieDatabase { } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs index a76feb8e57..1cf88b8bf9 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -6,13 +6,27 @@ namespace Neo.UnitTests.Trie.MPT [TestClass] public class UT_Helper { + [TestMethod] + public void TestConcat() + { + var a = new byte[]{0x01}; + var b = new byte[]{0x02}; + a = a.Concat(b); + Assert.AreEqual(2, a.Length); + } + [TestMethod] public void TestAdd() { var a = "ab".HexToBytes(); byte b = 0x0c; - a.Add(b); - Assert.AreEqual("ab", a.ToHexString()); + a = a.Add(b); + Assert.AreEqual("ab0c", a.ToHexString()); + + a = new byte[0]; + a = a.Add(b); + Assert.AreEqual(1, a.Length); + Assert.AreEqual("0c", a.ToHexString()); } [TestMethod] @@ -21,6 +35,12 @@ public void TestSkip() var s = "abcd01".HexToBytes(); s = s.Skip(2); Assert.AreEqual("01", s.ToHexString()); + + s = new byte[]{0x01}; + s = s.Skip(1); + Assert.AreEqual(0, s.Length); + s = s.Skip(2); + Assert.AreEqual(0, s.Length); } } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs new file mode 100644 index 0000000000..b54cfe574f --- /dev/null +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs @@ -0,0 +1,27 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Trie.MPT; +using System.Text; + +namespace Neo.UnitTests.Trie.MPT +{ + [TestClass] + public class UT_MPTNode + { + [TestMethod] + public void TestDecode() + { + var n = new ValueNode(); + n.Value = Encoding.ASCII.GetBytes("hello"); + var code = n.Encode(); + var m = MPTNode.Decode(code); + Assert.IsInstanceOfType(m, n.GetType()); + } + + [TestMethod] + public void TestFlag() + { + var n = new ShortNode(); + Assert.IsTrue(n.Flag.Dirty); + } + } +} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 63754df43b..9f9ddfb568 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -1,37 +1,58 @@ +using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Trie.MPT; +using Neo.Persistence; +using Neo.IO; +using System; namespace Neo.UnitTests.Trie.MPT { [TestClass] public class UT_MPTTrie { - private static Node root; + private MPTNode root; [ClassInitialize] public static void ClassInit(TestContext context) { - var r = new ExtensionNode(); - var b = new BranchNode(); - var l1 = new LeafNode(); - var l2 = new LeafNode(); - var v1 = new ValueNode("abcd".HexToBytes()); - var v2 = new ValueNode("2222".HexToBytes()); + + } + + [TestInitialize] + public void TestInit() + { + var r = new ShortNode(); r.Key = "0a0c".HexToBytes(); + var b = new FullNode(); + var l1 = new ShortNode(); l1.Key = new byte[]{0x01}; + var l2 = new ShortNode(); l2.Key = new byte[]{0x09}; + var v1 = new ValueNode(); + v1.Value = "abcd".HexToBytes(); + var v2 = new ValueNode(); + v2.Value = "2222".HexToBytes(); + var h1 = new HashNode(); + h1.Hash = Encoding.ASCII.GetBytes("hello"); + var l3 = new ShortNode(); + l3.Next = h1; + l3.Key = "0e".HexToBytes(); + r.Next = b; b.Children[0] = l1; - l1.Value = v1; + l1.Next = v1; b.Children[9] = l2; - l2.Value = v2; - root = r; + l2.Next = v2; + b.Children[10] = l3; + root = r; } [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(null, root); + var store = new MemoryStore(); + var mptdb = new MPTDatabase(store); + var mpt = new MPTTrie(mptdb, root); var result = mpt.TryGet("0a0c0001".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.AreEqual("abcd", value.ToHexString()); @@ -50,7 +71,85 @@ public void TestTryGet() Assert.IsFalse(result); result = mpt.TryGet("0a0c090901".HexToBytes(), out value); + Assert.AreEqual(false, result); + } + + [TestMethod] + public void TestTryGetResolve() + { + var n = new ValueNode(); + n.Value = Encoding.ASCII.GetBytes("hello"); + var store = new MemoryStore(); + store.Put(MPTDatabase.TABLE, n.GetHash(), n.Encode()); + var mptdb = new MPTDatabase(store); + var mpt = new MPTTrie(mptdb, root); + var result = mpt.TryGet("0a0c0a0e".HexToBytes(), out byte[] value); + + Assert.IsTrue(result); + Assert.IsTrue(value.Equal(n.Value)); + } + + [TestMethod] + public void TestTryPut() + { + var store = new MemoryStore(); + var mptdb = new MPTDatabase(store); + var sn = (ShortNode)root; + Assert.IsTrue(root is ShortNode); + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", sn.GetHash().ToHexString()); + var mpt = new MPTTrie(mptdb, new byte[]{}); + mpt.Put("0a0c0001".HexToBytes(), "abcd".HexToBytes()); + mpt.Put("0a0c0909".HexToBytes(), "2222".HexToBytes()); + Assert.AreEqual("{\"key\":\"0a0c\",\"next\":{\"children\":[{\"key\":\"01\",\"next\":{\"value\":\"abcd\"}},{},{},{},{},{},{},{},{},{\"key\":\"09\",\"next\":{\"value\":\"2222\"}},{},{},{},{},{},{},{}]}}", mpt.ToJson().ToString()); + mpt.Put("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); + Assert.AreEqual(8, store.Size(MPTDatabase.TABLE)); + } + + [TestMethod] + public void TestTryDelete() + { + var store = new MemoryStore(); + var mptdb = new MPTDatabase(store); + + var r1 = new ShortNode(); + r1.Key = "0a0c0001".HexToBytes(); + + + var r = new ShortNode(); + r.Key = "0a0c".HexToBytes(); + + + var b = new FullNode(); + r.Next = b; + + var l1 = new ShortNode(); + l1.Key = new byte[]{0x01}; + var v1 = new ValueNode(); + v1.Value = "abcd".HexToBytes(); + l1.Next = v1; + b.Children[0] = l1; + + var l2 = new ShortNode(); + l2.Key = new byte[]{0x09}; + var v2 = new ValueNode(); + v2.Value = "2222".HexToBytes(); + l2.Next = v2; + b.Children[9] = l2; + + r1.Next = v1; + Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", r1.GetHash().ToHexString()); + Assert.AreEqual("f3ad94e8fb6e1e85a8b573b2343845e3b0e0b96b61fcd0e20b6df159fde137a7", r.GetHash().ToHexString()); + + var mpt = new MPTTrie(mptdb, r); + var result = true; + result = mpt.TryGet("0a0c0909".HexToBytes(), out byte[] value); + Assert.IsTrue(result); + result = mpt.TryDelete("0a0c0909".HexToBytes()); + Assert.IsTrue(result); + result = mpt.TryDelete("0a0c0a0e".HexToBytes()); Assert.IsFalse(result); + Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", mpt.GetRoot().ToHexString()); } } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Node.cs b/tests/neo.UnitTests/Trie/MPT/UT_Node.cs deleted file mode 100644 index e69de29bb2..0000000000 From a1db507aca7ebbfefa26a11a157de9f64ab1bdeb Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 10:32:57 +0800 Subject: [PATCH 015/171] split nodes --- src/neo/Trie/MPT/MPTDatabase.cs | 6 +- src/neo/Trie/MPT/MPTNode.cs | 309 --------------------- src/neo/Trie/MPT/MPTNode/FullNode.cs | 73 +++++ src/neo/Trie/MPT/MPTNode/HashNode.cs | 55 ++++ src/neo/Trie/MPT/MPTNode/MPTNode.cs | 106 +++++++ src/neo/Trie/MPT/MPTNode/ShortNode.cs | 56 ++++ src/neo/Trie/MPT/MPTNode/ValueNode.cs | 46 +++ src/neo/Trie/MPT/MPTTrie.cs | 10 +- src/neo/Trie/Trie.cs | 12 +- src/neo/Trie/TrieDb.cs | 8 - tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 9 +- 11 files changed, 364 insertions(+), 326 deletions(-) delete mode 100644 src/neo/Trie/MPT/MPTNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/FullNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/HashNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/MPTNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/ShortNode.cs create mode 100644 src/neo/Trie/MPT/MPTNode/ValueNode.cs delete mode 100644 src/neo/Trie/TrieDb.cs diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 0601537db3..3a34a60ce7 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,11 +1,9 @@ using Neo.Persistence; -using System.Threading; -using System; namespace Neo.Trie.MPT { - public class MPTDatabase: ITrieDatabase + public class MPTDatabase { private IStore store; @@ -34,7 +32,7 @@ public void Delete(byte[] hash) } public void Put(MPTNode node) - { + { store.Put(TABLE, StoreKey(node.GetHash()), node.Encode()); } } diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs deleted file mode 100644 index 647fd1f07f..0000000000 --- a/src/neo/Trie/MPT/MPTNode.cs +++ /dev/null @@ -1,309 +0,0 @@ -using System.IO; -using System.Text; -using Neo.IO; -using Neo.IO.Json; -using Neo.Cryptography; - -namespace Neo.Trie.MPT -{ - public enum NodeType - { - FullNode, - ShortNode, - HashNode, - ValueNode, - NullNode = 0xFF - } - - public class NodeFlag - { - public byte[] Hash; - public bool Dirty; - - public NodeFlag() - { - Dirty = true; - } - } - - public abstract class MPTNode: ISerializable - { - public NodeFlag Flag; - protected NodeType nType; - - protected abstract byte[] calHash(); - - public virtual byte[] GetHash() { - if (!Flag.Dirty) return Flag.Hash; - Flag.Hash = calHash(); - Flag.Dirty = false; - return (byte[])Flag.Hash.Clone(); - } - - public void ResetFlag() - { - Flag = new NodeFlag(); - } - - public int Size { get; } - - public MPTNode() - { - Flag = new NodeFlag(); - } - - public virtual void Serialize(BinaryWriter writer) - { - writer.Write((byte)nType); - } - - public virtual void Deserialize(BinaryReader reader) - { - - } - - public byte[] Encode() - { - return this.ToArray(); - } - - public static MPTNode Decode(byte[] data) - { - var nodeType = (NodeType)data[0]; - data = data.Skip(1); - - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) - { - switch (nodeType) - { - case NodeType.FullNode: - { - var n = new FullNode(); - n.Deserialize(reader); - return n; - } - case NodeType.ShortNode: - { - var n = new ShortNode(); - n.Deserialize(reader); - return n; - } - case NodeType.ValueNode: - { - var n = new ValueNode(); - n.Deserialize(reader); - return n; - } - default: - throw new System.Exception(); - } - } - } - - public abstract JObject ToJson(); - } - - public class ShortNode : MPTNode - { - public byte[] Key; - - public MPTNode Next; - - public new int Size => Key.Length + Next.Size; - - protected override byte[] calHash(){ - return Key.Concat(Next.GetHash()).Sha256(); - } - public ShortNode() - { - nType = NodeType.ShortNode; - } - - public ShortNode Clone() - { - var cloned = new ShortNode() { - Key = (byte[])Key.Clone(), - Next = Next, - }; - return cloned; - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.WriteVarBytes(Key); - writer.WriteVarBytes(Next.GetHash()); - } - - public override void Deserialize(BinaryReader reader) - { - var hashNode = new HashNode(); - Key = reader.ReadVarBytes(); - hashNode.Deserialize(reader); - Next = hashNode; - } - - public override JObject ToJson() - { - var json = new JObject(); - json["key"] = Key.ToHexString(); - json["next"] = Next.ToJson(); - return json; - } - } - - public class FullNode : MPTNode - { - public MPTNode[] Children = new MPTNode[17]; - - public new int Size; - - public FullNode() - { - nType = NodeType.FullNode; - for (int i = 0; i < Children.Length; i++) - { - Children[i] = HashNode.EmptyNode(); - } - } - - protected override byte[] calHash() - { - var bytes = new byte[0]; - for (int i = 0; i < Children.Length; i++) - { - bytes = bytes.Concat(Children[i].GetHash()); - } - return bytes.Sha256(); - } - - public FullNode Clone() - { - var cloned = new FullNode(); - for (int i = 0; i < Children.Length; i++) - { - cloned.Children[i] = Children[i]; - } - return cloned; - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - for (int i = 0; i < Children.Length; i++) - { - writer.WriteVarBytes(Children[i].GetHash()); - } - } - - public override void Deserialize(BinaryReader reader) - { - for (int i = 0; i < Children.Length; i++) - { - var hashNode = new HashNode(reader.ReadVarBytes()); - Children[i] = hashNode; - } - } - - public override JObject ToJson() - { - var json = new JObject(); - var jchildren = new JArray(); - for (int i = 0; i < Children.Length; i++) - { - jchildren.Add(Children[i].ToJson()); - } - json["children"] = jchildren; - return json; - } - } - - public class HashNode : MPTNode - { - public byte[] Hash; - - public HashNode() - { - nType = NodeType.HashNode; - } - - public HashNode(byte[] hash) - { - nType = NodeType.HashNode; - Hash = (byte[])hash.Clone(); - } - - protected override byte[] calHash() - { - return (byte[])Hash.Clone(); - } - - public static HashNode EmptyNode() - { - return new HashNode(new byte[]{}); - } - - public bool IsEmptyNode => Hash.Length == 0; - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.WriteVarBytes(Hash); - } - - public override void Deserialize(BinaryReader reader) - { - Hash = reader.ReadVarBytes(); - } - - public override JObject ToJson() - { - var json = new JObject(); - if (!this.IsEmptyNode) - { - json["hash"] = Hash.ToHexString(); - } - return json; - } - } - - public class ValueNode : MPTNode - { - public byte[] Value; - - protected override byte[] calHash() - { - return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); - } - - public ValueNode() - { - nType = NodeType.ValueNode; - } - - public ValueNode(byte[] val) - { - nType = NodeType.ValueNode; - Value = (byte[])val.Clone(); - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.WriteVarBytes(Value); - } - - public override void Deserialize(BinaryReader reader) - { - Value = reader.ReadVarBytes(); - } - - public override JObject ToJson() - { - var json = new JObject(); - json["value"] = Value.ToHexString(); - return json; - } - } -} diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs new file mode 100644 index 0000000000..9dba385192 --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -0,0 +1,73 @@ +using System.IO; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public class FullNode : MPTNode + { + public MPTNode[] Children = new MPTNode[17]; + + public new int Size; + + public FullNode() + { + nType = NodeType.FullNode; + for (int i = 0; i < Children.Length; i++) + { + Children[i] = HashNode.EmptyNode(); + } + } + + protected override byte[] calHash() + { + var bytes = new byte[0]; + for (int i = 0; i < Children.Length; i++) + { + bytes = bytes.Concat(Children[i].GetHash()); + } + return bytes.Sha256(); + } + + public FullNode Clone() + { + var cloned = new FullNode(); + for (int i = 0; i < Children.Length; i++) + { + cloned.Children[i] = Children[i]; + } + return cloned; + } + + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + for (int i = 0; i < Children.Length; i++) + { + writer.WriteVarBytes(Children[i].GetHash()); + } + } + + public override void Deserialize(BinaryReader reader) + { + for (int i = 0; i < Children.Length; i++) + { + var hashNode = new HashNode(reader.ReadVarBytes()); + Children[i] = hashNode; + } + } + + public override JObject ToJson() + { + var json = new JObject(); + var jchildren = new JArray(); + for (int i = 0; i < Children.Length; i++) + { + jchildren.Add(Children[i].ToJson()); + } + json["children"] = jchildren; + return json; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs new file mode 100644 index 0000000000..f4cb71ea61 --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -0,0 +1,55 @@ +using System.IO; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public class HashNode : MPTNode + { + public byte[] Hash; + + public HashNode() + { + nType = NodeType.HashNode; + } + + public HashNode(byte[] hash) + { + nType = NodeType.HashNode; + Hash = (byte[])hash.Clone(); + } + + protected override byte[] calHash() + { + return (byte[])Hash.Clone(); + } + + public static HashNode EmptyNode() + { + return new HashNode(new byte[]{}); + } + + public bool IsEmptyNode => Hash.Length == 0; + + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + writer.WriteVarBytes(Hash); + } + + public override void Deserialize(BinaryReader reader) + { + Hash = reader.ReadVarBytes(); + } + + public override JObject ToJson() + { + var json = new JObject(); + if (!this.IsEmptyNode) + { + json["hash"] = Hash.ToHexString(); + } + return json; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/MPTNode.cs b/src/neo/Trie/MPT/MPTNode/MPTNode.cs new file mode 100644 index 0000000000..104b57099c --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/MPTNode.cs @@ -0,0 +1,106 @@ +using System.IO; +using System.Text; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public enum NodeType + { + FullNode, + ShortNode, + HashNode, + ValueNode, + NullNode = 0xFF + } + + public class NodeFlag + { + public byte[] Hash; + public bool Dirty; + + public NodeFlag() + { + Dirty = true; + } + } + + public abstract class MPTNode: ISerializable + { + public NodeFlag Flag; + protected NodeType nType; + + protected abstract byte[] calHash(); + + public virtual byte[] GetHash() { + if (!Flag.Dirty) return Flag.Hash; + Flag.Hash = calHash(); + Flag.Dirty = false; + return (byte[])Flag.Hash.Clone(); + } + + public void ResetFlag() + { + Flag = new NodeFlag(); + } + + public int Size { get; } + + public MPTNode() + { + Flag = new NodeFlag(); + } + + public virtual void Serialize(BinaryWriter writer) + { + writer.Write((byte)nType); + } + + public virtual void Deserialize(BinaryReader reader) + { + + } + + public byte[] Encode() + { + return this.ToArray(); + } + + public static MPTNode Decode(byte[] data) + { + var nodeType = (NodeType)data[0]; + data = data.Skip(1); + + using (MemoryStream ms = new MemoryStream(data, false)) + using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) + { + switch (nodeType) + { + case NodeType.FullNode: + { + var n = new FullNode(); + n.Deserialize(reader); + return n; + } + case NodeType.ShortNode: + { + var n = new ShortNode(); + n.Deserialize(reader); + return n; + } + case NodeType.ValueNode: + { + var n = new ValueNode(); + n.Deserialize(reader); + return n; + } + default: + throw new System.Exception(); + } + } + } + + public abstract JObject ToJson(); + } +} diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs new file mode 100644 index 0000000000..87e63febd2 --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -0,0 +1,56 @@ +using System.IO; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public class ShortNode : MPTNode + { + public byte[] Key; + + public MPTNode Next; + + public new int Size => Key.Length + Next.Size; + + protected override byte[] calHash(){ + return Key.Concat(Next.GetHash()).Sha256(); + } + public ShortNode() + { + nType = NodeType.ShortNode; + } + + public ShortNode Clone() + { + var cloned = new ShortNode() { + Key = (byte[])Key.Clone(), + Next = Next, + }; + return cloned; + } + + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + writer.WriteVarBytes(Key); + writer.WriteVarBytes(Next.GetHash()); + } + + public override void Deserialize(BinaryReader reader) + { + var hashNode = new HashNode(); + Key = reader.ReadVarBytes(); + hashNode.Deserialize(reader); + Next = hashNode; + } + + public override JObject ToJson() + { + var json = new JObject(); + json["key"] = Key.ToHexString(); + json["next"] = Next.ToJson(); + return json; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs new file mode 100644 index 0000000000..b4c84cbe04 --- /dev/null +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -0,0 +1,46 @@ +using System.IO; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; + +namespace Neo.Trie.MPT +{ + public class ValueNode : MPTNode + { + public byte[] Value; + + protected override byte[] calHash() + { + return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); + } + + public ValueNode() + { + nType = NodeType.ValueNode; + } + + public ValueNode(byte[] val) + { + nType = NodeType.ValueNode; + Value = (byte[])val.Clone(); + } + + public override void Serialize(BinaryWriter writer) + { + base.Serialize(writer); + writer.WriteVarBytes(Value); + } + + public override void Deserialize(BinaryReader reader) + { + Value = reader.ReadVarBytes(); + } + + public override JObject ToJson() + { + var json = new JObject(); + json["value"] = Value.ToHexString(); + return json; + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index c003c80f2b..cc2c1bc2dd 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,5 +1,4 @@ using Neo.IO.Json; -using System; namespace Neo.Trie.MPT { @@ -89,7 +88,7 @@ private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) return false; } - public bool Put(byte[] path, byte[] value) + public bool TryPut(byte[] path, byte[] value) { var n = new ValueNode(value); path = (byte[])path.Clone(); @@ -320,9 +319,14 @@ public byte[] GetRoot() return this.root.GetHash(); } - public void Proof(byte[] path) + public bool Prove(byte[] key, byte[] proof) { + return true; + } + public byte[] GetProof(byte[] key, byte[] value) + { + return new byte[0]; } public JObject ToJson() diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index 4ee04fff7e..2c0919922f 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -3,6 +3,16 @@ namespace Neo.Trie { public interface ITrie { - + bool TryGet(byte[] path, out byte[] value); + + bool TryPut(byte[] path, byte[] value); + + bool TryDelete(byte[] path); + + byte[] GetRoot(); + + bool Prove(byte[] key, byte[] proof); + + byte[] GetProof(byte[] Key, byte [] value); } } \ No newline at end of file diff --git a/src/neo/Trie/TrieDb.cs b/src/neo/Trie/TrieDb.cs deleted file mode 100644 index 06f798eeea..0000000000 --- a/src/neo/Trie/TrieDb.cs +++ /dev/null @@ -1,8 +0,0 @@ - -namespace Neo.Trie -{ - public interface ITrieDatabase - { - - } -} \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 9f9ddfb568..8d615e1281 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -103,7 +103,6 @@ public void TestTryPut() Assert.AreEqual("{\"key\":\"0a0c\",\"next\":{\"children\":[{\"key\":\"01\",\"next\":{\"value\":\"abcd\"}},{},{},{},{},{},{},{},{},{\"key\":\"09\",\"next\":{\"value\":\"2222\"}},{},{},{},{},{},{},{}]}}", mpt.ToJson().ToString()); mpt.Put("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); - Assert.AreEqual(8, store.Size(MPTDatabase.TABLE)); } [TestMethod] @@ -151,5 +150,13 @@ public void TestTryDelete() Assert.IsFalse(result); Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", mpt.GetRoot().ToHexString()); } + + [TestMethod] + public void TestGetProof() + { + var store = new MemoryStore(); + var mptdb = new MPTDatabase(store); + var mpt = new MPTTrie(mptdb, new byte[]{}); + } } } \ No newline at end of file From ac3c4a8474471985758f230d9ab7fdba24798a87 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 15:13:20 +0800 Subject: [PATCH 016/171] add GetProof and ut --- src/neo/Trie/MPT/MPTDatabase.cs | 5 +- src/neo/Trie/MPT/{MPTNode => }/MPTNode.cs | 47 +++++++++---------- src/neo/Trie/MPT/MPTNode/HashNode.cs | 11 ----- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 3 +- src/neo/Trie/MPT/MPTTrie.cs | 53 ++++++++++++++++++++-- src/neo/Trie/Trie.cs | 6 +-- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 42 +++++++++++++++-- 7 files changed, 117 insertions(+), 50 deletions(-) rename src/neo/Trie/MPT/{MPTNode => }/MPTNode.cs (70%) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 3a34a60ce7..cf9efcb00b 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,5 +1,6 @@ using Neo.Persistence; +using System.Text; namespace Neo.Trie.MPT { @@ -9,9 +10,11 @@ public class MPTDatabase public static readonly byte TABLE = 0x4D; + public static readonly byte[] Prefix = Encoding.ASCII.GetBytes("MPT"); + private byte[] StoreKey(byte[] hash) { - return hash; + return Prefix.Concat(hash); } public MPTDatabase(IStore store) diff --git a/src/neo/Trie/MPT/MPTNode/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs similarity index 70% rename from src/neo/Trie/MPT/MPTNode/MPTNode.cs rename to src/neo/Trie/MPT/MPTNode.cs index 104b57099c..84f62b2107 100644 --- a/src/neo/Trie/MPT/MPTNode/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -25,15 +25,16 @@ public NodeFlag() Dirty = true; } } - - public abstract class MPTNode: ISerializable + + public abstract class MPTNode : ISerializable { public NodeFlag Flag; protected NodeType nType; protected abstract byte[] calHash(); - - public virtual byte[] GetHash() { + + public virtual byte[] GetHash() + { if (!Flag.Dirty) return Flag.Hash; Flag.Hash = calHash(); Flag.Dirty = false; @@ -46,22 +47,22 @@ public void ResetFlag() } public int Size { get; } - + public MPTNode() { Flag = new NodeFlag(); } - public virtual void Serialize(BinaryWriter writer) + public virtual void Serialize(BinaryWriter writer) { writer.Write((byte)nType); } public virtual void Deserialize(BinaryReader reader) { - + } - + public byte[] Encode() { return this.ToArray(); @@ -78,23 +79,23 @@ public static MPTNode Decode(byte[] data) switch (nodeType) { case NodeType.FullNode: - { - var n = new FullNode(); - n.Deserialize(reader); - return n; - } + { + var n = new FullNode(); + n.Deserialize(reader); + return n; + } case NodeType.ShortNode: - { - var n = new ShortNode(); - n.Deserialize(reader); - return n; - } + { + var n = new ShortNode(); + n.Deserialize(reader); + return n; + } case NodeType.ValueNode: - { - var n = new ValueNode(); - n.Deserialize(reader); - return n; - } + { + var n = new ValueNode(); + n.Deserialize(reader); + return n; + } default: throw new System.Exception(); } diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index f4cb71ea61..057ffa25f6 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -31,17 +31,6 @@ public static HashNode EmptyNode() public bool IsEmptyNode => Hash.Length == 0; - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.WriteVarBytes(Hash); - } - - public override void Deserialize(BinaryReader reader) - { - Hash = reader.ReadVarBytes(); - } - public override JObject ToJson() { var json = new JObject(); diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 87e63febd2..5564e2bbcf 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -39,9 +39,8 @@ public override void Serialize(BinaryWriter writer) public override void Deserialize(BinaryReader reader) { - var hashNode = new HashNode(); Key = reader.ReadVarBytes(); - hashNode.Deserialize(reader); + var hashNode = new HashNode(reader.ReadVarBytes()); Next = hashNode; } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index cc2c1bc2dd..8c42ebd631 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,4 +1,5 @@ using Neo.IO.Json; +using System.Collections.Generic; namespace Neo.Trie.MPT { @@ -225,7 +226,6 @@ private bool tryDelete(ref MPTNode node, byte[] path) { node = HashNode.EmptyNode(); result = true; - break; } break; } @@ -319,14 +319,57 @@ public byte[] GetRoot() return this.root.GetHash(); } - public bool Prove(byte[] key, byte[] proof) + public Dictionary GetProof(byte[] path) { - return true; + var dict = new Dictionary { }; + getProof(ref root, path, dict); + return dict; } - public byte[] GetProof(byte[] key, byte[] value) + private void getProof(ref MPTNode node, byte[] path, Dictionary dict) { - return new byte[0]; + switch (node) + { + case ValueNode valueNode: + { + if (path.Length == 0) + { + dict.Add(valueNode.GetHash(), valueNode.Encode()); + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) break; + node = Resolve(hashNode.Hash); + dict.Add(node.GetHash(), node.Encode()); + getProof(ref node, path, dict); + break; + } + case FullNode fullNode: + { + dict.Add(fullNode.GetHash(), fullNode.Encode()); + if (path.Length == 0) + { + getProof(ref fullNode.Children[16], path, dict); + } + else + { + getProof(ref fullNode.Children[path[0]], path.Skip(1), dict); + } + break; + } + case ShortNode shortNode: + { + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + dict.Add(shortNode.GetHash(), shortNode.Encode()); + getProof(ref shortNode.Next, path.Skip(prefix.Length), dict); + } + break; + } + } } public JObject ToJson() diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index 2c0919922f..37c059b245 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -1,4 +1,6 @@ +using System.Collections.Generic; + namespace Neo.Trie { public interface ITrie @@ -11,8 +13,6 @@ public interface ITrie byte[] GetRoot(); - bool Prove(byte[] key, byte[] proof); - - byte[] GetProof(byte[] Key, byte [] value); + Dictionary GetProof(byte[] Key); } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 8d615e1281..6c8f35f868 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -98,10 +98,9 @@ public void TestTryPut() Assert.IsTrue(root is ShortNode); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", sn.GetHash().ToHexString()); var mpt = new MPTTrie(mptdb, new byte[]{}); - mpt.Put("0a0c0001".HexToBytes(), "abcd".HexToBytes()); - mpt.Put("0a0c0909".HexToBytes(), "2222".HexToBytes()); - Assert.AreEqual("{\"key\":\"0a0c\",\"next\":{\"children\":[{\"key\":\"01\",\"next\":{\"value\":\"abcd\"}},{},{},{},{},{},{},{},{},{\"key\":\"09\",\"next\":{\"value\":\"2222\"}},{},{},{},{},{},{},{}]}}", mpt.ToJson().ToString()); - mpt.Put("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); + mpt.TryPut("0a0c0001".HexToBytes(), "abcd".HexToBytes()); + mpt.TryPut("0a0c0909".HexToBytes(), "2222".HexToBytes()); + mpt.TryPut("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); } @@ -154,9 +153,42 @@ public void TestTryDelete() [TestMethod] public void TestGetProof() { + var r = new ShortNode(); + r.Key = "0a0c".HexToBytes(); + var b = new FullNode(); + var l1 = new ShortNode(); + l1.Key = new byte[]{0x01}; + var l2 = new ShortNode(); + l2.Key = new byte[]{0x09}; + var v1 = new ValueNode(); + v1.Value = "abcd".HexToBytes(); + var v2 = new ValueNode(); + v2.Value = "2222".HexToBytes(); + var h1 = new HashNode(); + h1.Hash = Encoding.ASCII.GetBytes("hello"); + var l3 = new ShortNode(); + l3.Next = h1; + l3.Key = "0e".HexToBytes(); + + r.Next = b; + b.Children[0] = l1; + l1.Next = v1; + b.Children[9] = l2; + l2.Next = v2; + b.Children[10] = l3; + var store = new MemoryStore(); var mptdb = new MPTDatabase(store); - var mpt = new MPTTrie(mptdb, new byte[]{}); + var mpt = new MPTTrie(mptdb, r); + + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); + var dict = mpt.GetProof("0a0c0001".HexToBytes()); + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); + Assert.AreEqual(4, dict.Count); + Assert.IsTrue(dict.TryGetValue(mpt.GetRoot(), out byte[] value)); + Assert.IsTrue(dict.TryGetValue(b.GetHash(), out value)); + Assert.IsTrue(dict.TryGetValue(l1.GetHash(), out value)); + Assert.IsTrue(dict.TryGetValue(v1.GetHash(), out value)); } } } \ No newline at end of file From 6eeaa98a6e70beb6b1c1a170721e2e3c8c04b0f6 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 15:25:31 +0800 Subject: [PATCH 017/171] add ut --- tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 26 ++++++++++++++++++++++ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 3 +-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs index 1cf88b8bf9..5a2dc9cf1d 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -42,5 +42,31 @@ public void TestSkip() s = s.Skip(2); Assert.AreEqual(0, s.Length); } + + [TestMethod] + public void TestCommonPrefix() + { + var a = "1234abcd".HexToBytes(); + var b = "".HexToBytes(); + var prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + + b = "100000".HexToBytes(); + prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + + b = "1234".HexToBytes(); + prefix = a.CommonPrefix(b); + Assert.AreEqual("1234", prefix.ToHexString()); + + b = a; + prefix = a.CommonPrefix(b); + Assert.AreEqual("1234abcd", prefix.ToHexString()); + + a = new byte[0]; + b = new byte[0]; + prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + } } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 6c8f35f868..bbb455c59e 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -80,8 +80,8 @@ public void TestTryGetResolve() var n = new ValueNode(); n.Value = Encoding.ASCII.GetBytes("hello"); var store = new MemoryStore(); - store.Put(MPTDatabase.TABLE, n.GetHash(), n.Encode()); var mptdb = new MPTDatabase(store); + mptdb.Put(n); var mpt = new MPTTrie(mptdb, root); var result = mpt.TryGet("0a0c0a0e".HexToBytes(), out byte[] value); @@ -183,7 +183,6 @@ public void TestGetProof() Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); var dict = mpt.GetProof("0a0c0001".HexToBytes()); - Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); Assert.AreEqual(4, dict.Count); Assert.IsTrue(dict.TryGetValue(mpt.GetRoot(), out byte[] value)); Assert.IsTrue(dict.TryGetValue(b.GetHash(), out value)); From 2037f785e77504ec564e7c3d9bed8c653c12385f Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 15:41:54 +0800 Subject: [PATCH 018/171] format --- src/neo/Trie/MPT/Helper.cs | 7 ++++--- src/neo/Trie/MPT/MPTTrie.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs index acab94f28c..86f6175287 100644 --- a/src/neo/Trie/MPT/Helper.cs +++ b/src/neo/Trie/MPT/Helper.cs @@ -14,7 +14,7 @@ public static byte[] Concat(this byte[] a, byte[] b) public static byte[] CommonPrefix(this byte[] a, byte[] b) { - var prefix = new byte[]{}; + var prefix = new byte[] { }; var minLen = a.Length <= b.Length ? a.Length : b.Length; if (a.Length != 0 && b.Length != 0) @@ -40,9 +40,10 @@ public static bool Equal(this byte[] a, byte[] b) public static byte[] Skip(this byte[] a, int count) { - var result = new byte[]{}; + var result = new byte[] { }; var len = a.Length - count; - if (0 < len) { + if (0 < len) + { result = new byte[len]; Array.Copy(a, count, result, 0, len); } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 8c42ebd631..cd821b1d55 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -93,7 +93,7 @@ public bool TryPut(byte[] path, byte[] value) { var n = new ValueNode(value); path = (byte[])path.Clone(); - if (0 == value.Length) + if (value.Length == 0) { return tryDelete(ref root, path); } From 260b1c11cb129a89825230ef329ce70f2aac8cad Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 16 Jan 2020 16:17:42 +0800 Subject: [PATCH 019/171] fix some --- src/neo/Trie/MPT/MPTTrie.cs | 94 ++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index cd821b1d55..9872d38864 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -102,42 +102,44 @@ public bool TryPut(byte[] path, byte[] value) private bool put(ref MPTNode node, byte[] path, MPTNode val) { - var result = false; - var oldHash = node.GetHash(); switch (node) { case ValueNode valueNode: { if (path.Length == 0 && val is ValueNode vn) { + db.Delete(node.GetHash()); node = val; - node.ResetFlag(); db.Put(node); - result = true; - break; + return true; } - break; + return false; } case ShortNode shortNode: { var prefix = shortNode.Key.CommonPrefix(path); + var oldHash = shortNode.GetHash(); if (prefix.Length == shortNode.Key.Length) { - result = put(ref shortNode.Next, path.Skip(prefix.Length), val); + var result = put(ref shortNode.Next, path.Skip(prefix.Length), val); if (result) { + db.Delete(oldHash); shortNode.ResetFlag(); db.Put(shortNode); } - break; + return result; } var pathRemain = path.Skip(prefix.Length); var keyRemain = shortNode.Key.Skip(prefix.Length); var son = new FullNode(); MPTNode grandSon1 = HashNode.EmptyNode(), grandSon2 = HashNode.EmptyNode(); + put(ref grandSon1, keyRemain.Skip(1), shortNode.Next); + db.Put(grandSon1); son.Children[keyRemain[0]] = grandSon1; + if (pathRemain.Length == 0) { put(ref grandSon2, pathRemain, val); @@ -148,6 +150,7 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) put(ref grandSon2, pathRemain.Skip(1), val); son.Children[pathRemain[0]] = grandSon2; } + db.Put(grandSon2); db.Put(son); if (0 < prefix.Length) { @@ -156,18 +159,20 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) Key = prefix, Next = son, }; + db.Put(extensionNode); node = extensionNode; - db.Put(node); } else { node = son; } - result = true; - break; + db.Delete(oldHash); + return true; } case FullNode fullNode: { + var result = false; + var oldHash = fullNode.GetHash(); if (path.Length == 0) { result = put(ref fullNode.Children[fullNode.Children.Length], path, val); @@ -178,10 +183,11 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) } if (result) { + db.Delete(oldHash); fullNode.ResetFlag(); db.Put(fullNode); } - break; + return result; } case HashNode hashNode: { @@ -194,18 +200,14 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) }; node = newNode; db.Put(node); - result = true; - break; + return true; } node = Resolve(hashNode.Hash); - result = put(ref node, path, val); - break; + return put(ref node, path, val); } default: throw new System.Exception(); } - if (result) db.Delete(oldHash); - return result; } public bool TryDelete(byte[] path) @@ -215,47 +217,51 @@ public bool TryDelete(byte[] path) private bool tryDelete(ref MPTNode node, byte[] path) { - var result = false; - var oldHash = node.GetHash(); - switch (node) { case ValueNode valueNode: { if (path.Length == 0) { + db.Delete(valueNode.GetHash()); node = HashNode.EmptyNode(); - result = true; + return true; } - break; + return false; } case ShortNode shortNode: { var prefix = shortNode.Key.CommonPrefix(path); + var oldHash = shortNode.GetHash(); if (prefix.Length == shortNode.Key.Length) { - result = tryDelete(ref shortNode.Next, path.Skip(prefix.Length)); - if (!result) break; + var result = tryDelete(ref shortNode.Next, path.Skip(prefix.Length)); + if (!result) return false; + db.Delete(oldHash); if (shortNode.Next is HashNode hashNode && hashNode.IsEmptyNode) { node = shortNode.Next; - db.Put(node); } - if (shortNode.Next is ShortNode sn) + else if (shortNode.Next is ShortNode sn) { shortNode.Key = shortNode.Key.Concat(sn.Key); shortNode.Next = sn.Next; shortNode.ResetFlag(); db.Put(shortNode); } - result = true; - break; + else + { + node.ResetFlag(); + db.Put(shortNode); + } + return true; } - result = false; - break; + return false; } case FullNode fullNode: { + var result = false; + var oldHash = fullNode.GetHash(); if (path.Length == 0) { result = tryDelete(ref fullNode.Children[fullNode.Children.Length], path); @@ -264,7 +270,8 @@ private bool tryDelete(ref MPTNode node, byte[] path) { result = tryDelete(ref fullNode.Children[path[0]], path.Skip(1)); } - if (!result) break; + if (!result) return false; + db.Delete(oldHash); var nonEmptyChildren = new byte[] { }; for (int i = 0; i < fullNode.Children.Length; i++) { @@ -275,18 +282,20 @@ private bool tryDelete(ref MPTNode node, byte[] path) { fullNode.ResetFlag(); db.Put(fullNode); - break; + return true; } var childIndex = nonEmptyChildren[0]; var child = fullNode.Children[childIndex]; - if (child is HashNode hashNode) child = Resolve(hashNode.Hash); + if (child is HashNode hashNode) + child = Resolve(hashNode.Hash); if (child is ShortNode shortNode) { db.Delete(shortNode.GetHash()); shortNode.Key = nonEmptyChildren.Concat(shortNode.Key); + shortNode.ResetFlag(); db.Put(shortNode); - node = child; - break; + node = shortNode; + return true; } var newNode = new ShortNode() { @@ -295,23 +304,20 @@ private bool tryDelete(ref MPTNode node, byte[] path) }; node = newNode; db.Put(node); - result = true; - break; + return true; } case HashNode hashNode: { if (hashNode.IsEmptyNode) { - result = false; - break; + return true; } node = Resolve(hashNode.Hash); - result = tryDelete(ref node, path); - break; + return tryDelete(ref node, path); } + default: + return false; } - if (result) db.Delete(oldHash); - return result; } public byte[] GetRoot() From 2381013c3be0e43eaa0802c2e432b12fd4c416d2 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 17 Jan 2020 15:54:39 +0800 Subject: [PATCH 020/171] fix interface --- src/neo/Trie/MPT/Helper.cs | 11 ++++++ src/neo/Trie/MPT/MPTDatabase.cs | 20 +++++++++-- src/neo/Trie/MPT/MPTTrie.cs | 34 +++++++++---------- src/neo/Trie/Trie.cs | 2 ++ tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 8 +++++ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 39 ++++++++++++---------- 6 files changed, 75 insertions(+), 39 deletions(-) diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs index 86f6175287..6b6a4c0445 100644 --- a/src/neo/Trie/MPT/Helper.cs +++ b/src/neo/Trie/MPT/Helper.cs @@ -57,5 +57,16 @@ public static byte[] Add(this byte[] a, byte b) result[a.Length] = b; return result; } + + public static byte[] ToNibbles(this byte[] path) + { + var result = new byte[path.Length * 2]; + for (int i = 0; i < path.Length; i++) + { + result[i * 2] = (byte)(path[i] >> 4); + result[i * 2 + 1] = (byte)(path[i] & 0x0F); + } + return result; + } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index cf9efcb00b..0563721ead 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,12 +1,13 @@ using Neo.Persistence; using System.Text; +using System; namespace Neo.Trie.MPT { public class MPTDatabase { - private IStore store; + private ISnapshot store; public static readonly byte TABLE = 0x4D; @@ -17,7 +18,7 @@ private byte[] StoreKey(byte[] hash) return Prefix.Concat(hash); } - public MPTDatabase(IStore store) + public MPTDatabase(ISnapshot store) { this.store = store; } @@ -38,5 +39,20 @@ public void Put(MPTNode node) { store.Put(TABLE, StoreKey(node.GetHash()), node.Encode()); } + + public void PutRoot(byte[] root) + { + store.Put(TABLE, StoreKey(Encoding.ASCII.GetBytes("mpt_root")), root); + } + + public byte[] GetRoot() + { + return store.TryGet(TABLE, StoreKey(Encoding.ASCII.GetBytes("mpt_root"))); + } + + public void Commit() + { + store.Commit(); + } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 9872d38864..6828ba9ee1 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,4 +1,5 @@ using Neo.IO.Json; +using Neo.Persistence; using System.Collections.Generic; namespace Neo.Trie.MPT @@ -8,33 +9,19 @@ public class MPTTrie : ITrie private MPTDatabase db; private MPTNode root; - public MPTTrie(MPTDatabase db, byte[] root) + public MPTTrie(MPTDatabase db) { if (db is null) throw new System.Exception(); this.db = db; - if (root.Length == 0) + var rbytes = db.GetRoot(); + if (rbytes.Length == 0) { this.root = HashNode.EmptyNode(); } else { - this.root = Resolve(root); - } - } - - public MPTTrie(MPTDatabase db, MPTNode root) - { - if (db is null) - throw new System.Exception(); - this.db = db; - if (root is null) - { - this.root = HashNode.EmptyNode(); - } - else - { - this.root = root; + this.root = Resolve(rbytes); } } @@ -45,6 +32,7 @@ public MPTNode Resolve(byte[] hash) public bool TryGet(byte[] path, out byte[] value) { + path = path.ToNibbles(); return tryGet(ref root, path, out value); } @@ -92,7 +80,7 @@ private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) public bool TryPut(byte[] path, byte[] value) { var n = new ValueNode(value); - path = (byte[])path.Clone(); + path = path.ToNibbles(); if (value.Length == 0) { return tryDelete(ref root, path); @@ -212,6 +200,7 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) public bool TryDelete(byte[] path) { + path = path.ToNibbles(); return tryDelete(ref root, path); } @@ -328,6 +317,7 @@ public byte[] GetRoot() public Dictionary GetProof(byte[] path) { var dict = new Dictionary { }; + path = path.ToNibbles(); getProof(ref root, path, dict); return dict; } @@ -378,6 +368,12 @@ private void getProof(ref MPTNode node, byte[] path, Dictionary } } + public void Commit() + { + db.PutRoot(GetRoot()); + db.Commit(); + } + public JObject ToJson() { return root.ToJson(); diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index 37c059b245..decd4ac912 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -14,5 +14,7 @@ public interface ITrie byte[] GetRoot(); Dictionary GetProof(byte[] Key); + + void Commit(); } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs index 5a2dc9cf1d..a826bf04d8 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -68,5 +68,13 @@ public void TestCommonPrefix() prefix = a.CommonPrefix(b); Assert.IsTrue(prefix.Length == 0); } + + [TestMethod] + public void TestToNibbles() + { + var a = "1234abcd".HexToBytes(); + var n = a.ToNibbles(); + Assert.AreEqual("010203040a0b0c0d", n.ToHexString()); + } } } \ No newline at end of file diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index bbb455c59e..9c0c11a298 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -12,6 +12,8 @@ public class UT_MPTTrie { private MPTNode root; + private MPTDatabase mptdb; + [ClassInitialize] public static void ClassInit(TestContext context) { @@ -45,14 +47,23 @@ public void TestInit() l2.Next = v2; b.Children[10] = l3; root = r; + var store = new MemoryStore(); + this.mptdb = new MPTDatabase(store.GetSnapshot()); + mptdb.PutRoot(root.GetHash()); + mptdb.Put(r); + mptdb.Put(b); + mptdb.Put(l1); + mptdb.Put(l2); + mptdb.Put(l3); + mptdb.Put(v1); + mptdb.Put(v2); } [TestMethod] public void TestTryGet() { - var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); - var mpt = new MPTTrie(mptdb, root); + + var mpt = new MPTTrie(mptdb); var result = mpt.TryGet("0a0c0001".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.AreEqual("abcd", value.ToHexString()); @@ -79,10 +90,8 @@ public void TestTryGetResolve() { var n = new ValueNode(); n.Value = Encoding.ASCII.GetBytes("hello"); - var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); mptdb.Put(n); - var mpt = new MPTTrie(mptdb, root); + var mpt = new MPTTrie(mptdb); var result = mpt.TryGet("0a0c0a0e".HexToBytes(), out byte[] value); Assert.IsTrue(result); @@ -93,11 +102,10 @@ public void TestTryGetResolve() public void TestTryPut() { var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); - var sn = (ShortNode)root; - Assert.IsTrue(root is ShortNode); - Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", sn.GetHash().ToHexString()); - var mpt = new MPTTrie(mptdb, new byte[]{}); + var db = new MPTDatabase(store.GetSnapshot()); + var mpt1 = new MPTTrie(mptdb); + Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt1.GetRoot().ToHexString()); + var mpt = new MPTTrie(db); mpt.TryPut("0a0c0001".HexToBytes(), "abcd".HexToBytes()); mpt.TryPut("0a0c0909".HexToBytes(), "2222".HexToBytes()); mpt.TryPut("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); @@ -107,9 +115,6 @@ public void TestTryPut() [TestMethod] public void TestTryDelete() { - var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); - var r1 = new ShortNode(); r1.Key = "0a0c0001".HexToBytes(); @@ -139,7 +144,7 @@ public void TestTryDelete() Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", r1.GetHash().ToHexString()); Assert.AreEqual("f3ad94e8fb6e1e85a8b573b2343845e3b0e0b96b61fcd0e20b6df159fde137a7", r.GetHash().ToHexString()); - var mpt = new MPTTrie(mptdb, r); + var mpt = new MPTTrie(mptdb); var result = true; result = mpt.TryGet("0a0c0909".HexToBytes(), out byte[] value); Assert.IsTrue(result); @@ -177,9 +182,7 @@ public void TestGetProof() l2.Next = v2; b.Children[10] = l3; - var store = new MemoryStore(); - var mptdb = new MPTDatabase(store); - var mpt = new MPTTrie(mptdb, r); + var mpt = new MPTTrie(mptdb); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); var dict = mpt.GetProof("0a0c0001".HexToBytes()); From d47429f404dc22df6101139cdf6071406594c7cf Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 17 Jan 2020 16:40:21 +0800 Subject: [PATCH 021/171] format --- src/neo/Trie/MPT/MPTDatabase.cs | 1 - src/neo/Trie/MPT/MPTNode.cs | 5 +---- src/neo/Trie/MPT/MPTNode/FullNode.cs | 13 ------------- src/neo/Trie/MPT/MPTNode/HashNode.cs | 19 +++---------------- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 15 ++++----------- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 8 -------- src/neo/Trie/MPT/MPTTrie.cs | 9 +-------- 7 files changed, 9 insertions(+), 61 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 0563721ead..478a6f4e11 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,7 +1,6 @@ using Neo.Persistence; using System.Text; -using System; namespace Neo.Trie.MPT { diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 84f62b2107..e695b82cb6 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -2,7 +2,6 @@ using System.Text; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -60,7 +59,7 @@ public virtual void Serialize(BinaryWriter writer) public virtual void Deserialize(BinaryReader reader) { - + } public byte[] Encode() @@ -101,7 +100,5 @@ public static MPTNode Decode(byte[] data) } } } - - public abstract JObject ToJson(); } } diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 9dba385192..76cf7ddedc 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -1,7 +1,6 @@ using System.IO; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -57,17 +56,5 @@ public override void Deserialize(BinaryReader reader) Children[i] = hashNode; } } - - public override JObject ToJson() - { - var json = new JObject(); - var jchildren = new JArray(); - for (int i = 0; i < Children.Length; i++) - { - jchildren.Add(Children[i].ToJson()); - } - json["children"] = jchildren; - return json; - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index 057ffa25f6..6837763a9e 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -1,6 +1,3 @@ -using System.IO; -using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -11,14 +8,14 @@ public class HashNode : MPTNode public HashNode() { nType = NodeType.HashNode; - } + } public HashNode(byte[] hash) { nType = NodeType.HashNode; Hash = (byte[])hash.Clone(); } - + protected override byte[] calHash() { return (byte[])Hash.Clone(); @@ -26,19 +23,9 @@ protected override byte[] calHash() public static HashNode EmptyNode() { - return new HashNode(new byte[]{}); + return new HashNode(new byte[] { }); } public bool IsEmptyNode => Hash.Length == 0; - - public override JObject ToJson() - { - var json = new JObject(); - if (!this.IsEmptyNode) - { - json["hash"] = Hash.ToHexString(); - } - return json; - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 5564e2bbcf..891b450095 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -1,7 +1,6 @@ using System.IO; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -13,7 +12,8 @@ public class ShortNode : MPTNode public new int Size => Key.Length + Next.Size; - protected override byte[] calHash(){ + protected override byte[] calHash() + { return Key.Concat(Next.GetHash()).Sha256(); } public ShortNode() @@ -23,7 +23,8 @@ public ShortNode() public ShortNode Clone() { - var cloned = new ShortNode() { + var cloned = new ShortNode() + { Key = (byte[])Key.Clone(), Next = Next, }; @@ -43,13 +44,5 @@ public override void Deserialize(BinaryReader reader) var hashNode = new HashNode(reader.ReadVarBytes()); Next = hashNode; } - - public override JObject ToJson() - { - var json = new JObject(); - json["key"] = Key.ToHexString(); - json["next"] = Next.ToJson(); - return json; - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index b4c84cbe04..e300620c2d 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -1,7 +1,6 @@ using System.IO; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; namespace Neo.Trie.MPT { @@ -35,12 +34,5 @@ public override void Deserialize(BinaryReader reader) { Value = reader.ReadVarBytes(); } - - public override JObject ToJson() - { - var json = new JObject(); - json["value"] = Value.ToHexString(); - return json; - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 6828ba9ee1..a82e364973 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,5 +1,3 @@ -using Neo.IO.Json; -using Neo.Persistence; using System.Collections.Generic; namespace Neo.Trie.MPT @@ -275,7 +273,7 @@ private bool tryDelete(ref MPTNode node, byte[] path) } var childIndex = nonEmptyChildren[0]; var child = fullNode.Children[childIndex]; - if (child is HashNode hashNode) + if (child is HashNode hashNode) child = Resolve(hashNode.Hash); if (child is ShortNode shortNode) { @@ -373,10 +371,5 @@ public void Commit() db.PutRoot(GetRoot()); db.Commit(); } - - public JObject ToJson() - { - return root.ToJson(); - } } } \ No newline at end of file From d6c91c7901a5fa7a90e5f13cef8ce679fa93a595 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 17 Jan 2020 16:41:23 +0800 Subject: [PATCH 022/171] rm nullnode typee --- src/neo/Trie/MPT/MPTNode.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index e695b82cb6..505a15926d 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -11,7 +11,6 @@ public enum NodeType ShortNode, HashNode, ValueNode, - NullNode = 0xFF } public class NodeFlag From 08acc30c8f1ec5f14ffe5b143a5596cff63b8741 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 10 Feb 2020 10:50:33 +0800 Subject: [PATCH 023/171] rm fullnode size --- src/neo/Trie/MPT/MPTNode/FullNode.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 76cf7ddedc..ff3f493d2a 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -8,8 +8,6 @@ public class FullNode : MPTNode { public MPTNode[] Children = new MPTNode[17]; - public new int Size; - public FullNode() { nType = NodeType.FullNode; From 5be8a6d4b859b18ea44db633938dffb7dbb7fef1 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 10 Feb 2020 14:22:07 +0800 Subject: [PATCH 024/171] rm Database commit --- src/neo/Trie/MPT/MPTDatabase.cs | 5 ----- src/neo/Trie/MPT/MPTTrie.cs | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 478a6f4e11..51b09ed9c5 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -48,10 +48,5 @@ public byte[] GetRoot() { return store.TryGet(TABLE, StoreKey(Encoding.ASCII.GetBytes("mpt_root"))); } - - public void Commit() - { - store.Commit(); - } } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index a82e364973..019495105b 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -13,7 +13,7 @@ public MPTTrie(MPTDatabase db) throw new System.Exception(); this.db = db; var rbytes = db.GetRoot(); - if (rbytes.Length == 0) + if (rbytes is null || rbytes.Length == 0) { this.root = HashNode.EmptyNode(); } @@ -369,7 +369,6 @@ private void getProof(ref MPTNode node, byte[] path, Dictionary public void Commit() { db.PutRoot(GetRoot()); - db.Commit(); } } } \ No newline at end of file From 75cac28ac70e83c9938323bc993b390fbbc33e7c Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 11 Feb 2020 17:16:57 +0800 Subject: [PATCH 025/171] format --- src/neo/Trie/MPT/Helper.cs | 2 +- src/neo/Trie/MPT/MPTDatabase.cs | 2 +- src/neo/Trie/MPT/MPTNode/FullNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/HashNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 2 +- src/neo/Trie/MPT/MPTTrie.cs | 2 +- src/neo/Trie/Trie.cs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs index 6b6a4c0445..484cdf7128 100644 --- a/src/neo/Trie/MPT/Helper.cs +++ b/src/neo/Trie/MPT/Helper.cs @@ -69,4 +69,4 @@ public static byte[] ToNibbles(this byte[] path) return result; } } -} \ No newline at end of file +} diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 51b09ed9c5..25bd63e256 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -49,4 +49,4 @@ public byte[] GetRoot() return store.TryGet(TABLE, StoreKey(Encoding.ASCII.GetBytes("mpt_root"))); } } -} \ No newline at end of file +} diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index ff3f493d2a..3d1cf1f8fc 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -55,4 +55,4 @@ public override void Deserialize(BinaryReader reader) } } } -} \ No newline at end of file +} diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index 6837763a9e..9abae91905 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -28,4 +28,4 @@ public static HashNode EmptyNode() public bool IsEmptyNode => Hash.Length == 0; } -} \ No newline at end of file +} diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 891b450095..a43d0a908a 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -45,4 +45,4 @@ public override void Deserialize(BinaryReader reader) Next = hashNode; } } -} \ No newline at end of file +} diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index e300620c2d..98d4ed61b4 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -35,4 +35,4 @@ public override void Deserialize(BinaryReader reader) Value = reader.ReadVarBytes(); } } -} \ No newline at end of file +} diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 019495105b..30e6c70952 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -371,4 +371,4 @@ public void Commit() db.PutRoot(GetRoot()); } } -} \ No newline at end of file +} diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index decd4ac912..51aa65df90 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -17,4 +17,4 @@ public interface ITrie void Commit(); } -} \ No newline at end of file +} From 687e7d6d97396d4971ff081ce9ec9de91162910b Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 11 Feb 2020 17:20:36 +0800 Subject: [PATCH 026/171] CalHash --- src/neo/Trie/MPT/MPTNode.cs | 4 ++-- src/neo/Trie/MPT/MPTNode/FullNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/HashNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 505a15926d..a144a9708f 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -29,12 +29,12 @@ public abstract class MPTNode : ISerializable public NodeFlag Flag; protected NodeType nType; - protected abstract byte[] calHash(); + protected abstract byte[] CalHash(); public virtual byte[] GetHash() { if (!Flag.Dirty) return Flag.Hash; - Flag.Hash = calHash(); + Flag.Hash = CalHash(); Flag.Dirty = false; return (byte[])Flag.Hash.Clone(); } diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 3d1cf1f8fc..70ea8e48b9 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -17,7 +17,7 @@ public FullNode() } } - protected override byte[] calHash() + protected override byte[] CalHash() { var bytes = new byte[0]; for (int i = 0; i < Children.Length; i++) diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index 9abae91905..21ac37b11f 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -16,7 +16,7 @@ public HashNode(byte[] hash) Hash = (byte[])hash.Clone(); } - protected override byte[] calHash() + protected override byte[] CalHash() { return (byte[])Hash.Clone(); } diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index a43d0a908a..ea00893bfc 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -12,7 +12,7 @@ public class ShortNode : MPTNode public new int Size => Key.Length + Next.Size; - protected override byte[] calHash() + protected override byte[] CalHash() { return Key.Concat(Next.GetHash()).Sha256(); } diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index 98d4ed61b4..0b9f6c96ee 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -8,7 +8,7 @@ public class ValueNode : MPTNode { public byte[] Value; - protected override byte[] calHash() + protected override byte[] CalHash() { return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); } From 3989c1691c10cb168d469c8a9229ec776338c0b3 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 11 Feb 2020 17:39:08 +0800 Subject: [PATCH 027/171] rm Clone() --- src/neo/Trie/MPT/MPTNode/FullNode.cs | 10 ---------- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 10 ---------- 2 files changed, 20 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 70ea8e48b9..b5b4f783a6 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -27,16 +27,6 @@ protected override byte[] CalHash() return bytes.Sha256(); } - public FullNode Clone() - { - var cloned = new FullNode(); - for (int i = 0; i < Children.Length; i++) - { - cloned.Children[i] = Children[i]; - } - return cloned; - } - public override void Serialize(BinaryWriter writer) { base.Serialize(writer); diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index ea00893bfc..b8fa035207 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -21,16 +21,6 @@ public ShortNode() nType = NodeType.ShortNode; } - public ShortNode Clone() - { - var cloned = new ShortNode() - { - Key = (byte[])Key.Clone(), - Next = Next, - }; - return cloned; - } - public override void Serialize(BinaryWriter writer) { base.Serialize(writer); From 79ee48ba0260edbad2bf3fe0d8b243392c014ce2 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 11 Feb 2020 17:45:48 +0800 Subject: [PATCH 028/171] use const variable --- src/neo/Trie/MPT/MPTDatabase.cs | 6 ++++-- src/neo/Trie/MPT/MPTNode/FullNode.cs | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 25bd63e256..bc9615c132 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -12,6 +12,8 @@ public class MPTDatabase public static readonly byte[] Prefix = Encoding.ASCII.GetBytes("MPT"); + public static readonly byte[] ROOT_KEY = Encoding.ASCII.GetBytes("mpt_root"); + private byte[] StoreKey(byte[] hash) { return Prefix.Concat(hash); @@ -41,12 +43,12 @@ public void Put(MPTNode node) public void PutRoot(byte[] root) { - store.Put(TABLE, StoreKey(Encoding.ASCII.GetBytes("mpt_root")), root); + store.Put(TABLE, StoreKey(ROOT_KEY), root); } public byte[] GetRoot() { - return store.TryGet(TABLE, StoreKey(Encoding.ASCII.GetBytes("mpt_root"))); + return store.TryGet(TABLE, StoreKey(ROOT_KEY)); } } } diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index b5b4f783a6..6c2d566ff4 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -6,7 +6,9 @@ namespace Neo.Trie.MPT { public class FullNode : MPTNode { - public MPTNode[] Children = new MPTNode[17]; + public const int CHILD_COUNT = 17; + + public MPTNode[] Children = new MPTNode[CHILD_COUNT]; public FullNode() { From 63bd5784816929fa29df0514835b45222722593a Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 11 Feb 2020 17:48:01 +0800 Subject: [PATCH 029/171] new line in ut --- tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 2 +- tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs | 2 +- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs index a826bf04d8..c54213848f 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -77,4 +77,4 @@ public void TestToNibbles() Assert.AreEqual("010203040a0b0c0d", n.ToHexString()); } } -} \ No newline at end of file +} diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs index b54cfe574f..d4947ef556 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs @@ -24,4 +24,4 @@ public void TestFlag() Assert.IsTrue(n.Flag.Dirty); } } -} \ No newline at end of file +} diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 9c0c11a298..9538040cf3 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -193,4 +193,4 @@ public void TestGetProof() Assert.IsTrue(dict.TryGetValue(v1.GetHash(), out value)); } } -} \ No newline at end of file +} From 6fa37ef8bd0d9fd0aa71968318108e401c89ad9e Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 11 Feb 2020 17:52:17 +0800 Subject: [PATCH 030/171] fix some --- src/neo/Trie/MPT/MPTTrie.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 30e6c70952..8108d1b7ad 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -92,11 +92,11 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) { case ValueNode valueNode: { - if (path.Length == 0 && val is ValueNode vn) + if (path.Length == 0 && val is ValueNode) { db.Delete(node.GetHash()); node = val; - db.Put(node); + db.Put(valueNode); return true; } return false; From 40f3b7d2afc64408e396363ec5829f205a325961 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 12 Feb 2020 12:00:30 +0800 Subject: [PATCH 031/171] capitalize method name --- src/neo/Trie/MPT/MPTTrie.cs | 58 ++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 8108d1b7ad..fecc245c1d 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -31,10 +31,10 @@ public MPTNode Resolve(byte[] hash) public bool TryGet(byte[] path, out byte[] value) { path = path.ToNibbles(); - return tryGet(ref root, path, out value); + return TryGet(ref root, path, out value); } - private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) + private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) { switch (node) { @@ -51,22 +51,22 @@ private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) { if (hashNode.IsEmptyNode) break; node = Resolve(hashNode.Hash); - return tryGet(ref node, path, out value); + return TryGet(ref node, path, out value); } case FullNode fullNode: { if (path.Length == 0) { - return tryGet(ref fullNode.Children[16], path, out value); + return TryGet(ref fullNode.Children[16], path, out value); } - return tryGet(ref fullNode.Children[path[0]], path.Skip(1), out value); + return TryGet(ref fullNode.Children[path[0]], path.Skip(1), out value); } case ShortNode shortNode: { var prefix = shortNode.Key.CommonPrefix(path); if (prefix.Length == shortNode.Key.Length) { - return tryGet(ref shortNode.Next, path.Skip(prefix.Length), out value); + return TryGet(ref shortNode.Next, path.Skip(prefix.Length), out value); } break; } @@ -75,18 +75,18 @@ private bool tryGet(ref MPTNode node, byte[] path, out byte[] value) return false; } - public bool TryPut(byte[] path, byte[] value) + public bool Put(byte[] path, byte[] value) { var n = new ValueNode(value); path = path.ToNibbles(); if (value.Length == 0) { - return tryDelete(ref root, path); + return TryDelete(ref root, path); } - return put(ref root, path, n); + return Put(ref root, path, n); } - private bool put(ref MPTNode node, byte[] path, MPTNode val) + private bool Put(ref MPTNode node, byte[] path, MPTNode val) { switch (node) { @@ -107,7 +107,7 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) var oldHash = shortNode.GetHash(); if (prefix.Length == shortNode.Key.Length) { - var result = put(ref shortNode.Next, path.Skip(prefix.Length), val); + var result = Put(ref shortNode.Next, path.Skip(prefix.Length), val); if (result) { db.Delete(oldHash); @@ -122,18 +122,18 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) var son = new FullNode(); MPTNode grandSon1 = HashNode.EmptyNode(), grandSon2 = HashNode.EmptyNode(); - put(ref grandSon1, keyRemain.Skip(1), shortNode.Next); + Put(ref grandSon1, keyRemain.Skip(1), shortNode.Next); db.Put(grandSon1); son.Children[keyRemain[0]] = grandSon1; if (pathRemain.Length == 0) { - put(ref grandSon2, pathRemain, val); + Put(ref grandSon2, pathRemain, val); son.Children[son.Children.Length] = grandSon2; } else { - put(ref grandSon2, pathRemain.Skip(1), val); + Put(ref grandSon2, pathRemain.Skip(1), val); son.Children[pathRemain[0]] = grandSon2; } db.Put(grandSon2); @@ -161,11 +161,11 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) var oldHash = fullNode.GetHash(); if (path.Length == 0) { - result = put(ref fullNode.Children[fullNode.Children.Length], path, val); + result = Put(ref fullNode.Children[fullNode.Children.Length], path, val); } else { - result = put(ref fullNode.Children[path[0]], path.Skip(1), val); + result = Put(ref fullNode.Children[path[0]], path.Skip(1), val); } if (result) { @@ -189,7 +189,7 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) return true; } node = Resolve(hashNode.Hash); - return put(ref node, path, val); + return Put(ref node, path, val); } default: throw new System.Exception(); @@ -199,10 +199,10 @@ private bool put(ref MPTNode node, byte[] path, MPTNode val) public bool TryDelete(byte[] path) { path = path.ToNibbles(); - return tryDelete(ref root, path); + return TryDelete(ref root, path); } - private bool tryDelete(ref MPTNode node, byte[] path) + private bool TryDelete(ref MPTNode node, byte[] path) { switch (node) { @@ -222,7 +222,7 @@ private bool tryDelete(ref MPTNode node, byte[] path) var oldHash = shortNode.GetHash(); if (prefix.Length == shortNode.Key.Length) { - var result = tryDelete(ref shortNode.Next, path.Skip(prefix.Length)); + var result = TryDelete(ref shortNode.Next, path.Skip(prefix.Length)); if (!result) return false; db.Delete(oldHash); if (shortNode.Next is HashNode hashNode && hashNode.IsEmptyNode) @@ -251,11 +251,11 @@ private bool tryDelete(ref MPTNode node, byte[] path) var oldHash = fullNode.GetHash(); if (path.Length == 0) { - result = tryDelete(ref fullNode.Children[fullNode.Children.Length], path); + result = TryDelete(ref fullNode.Children[fullNode.Children.Length], path); } else { - result = tryDelete(ref fullNode.Children[path[0]], path.Skip(1)); + result = TryDelete(ref fullNode.Children[path[0]], path.Skip(1)); } if (!result) return false; db.Delete(oldHash); @@ -300,7 +300,7 @@ private bool tryDelete(ref MPTNode node, byte[] path) return true; } node = Resolve(hashNode.Hash); - return tryDelete(ref node, path); + return TryDelete(ref node, path); } default: return false; @@ -316,11 +316,11 @@ public Dictionary GetProof(byte[] path) { var dict = new Dictionary { }; path = path.ToNibbles(); - getProof(ref root, path, dict); + GetProof(ref root, path, dict); return dict; } - private void getProof(ref MPTNode node, byte[] path, Dictionary dict) + private void GetProof(ref MPTNode node, byte[] path, Dictionary dict) { switch (node) { @@ -337,7 +337,7 @@ private void getProof(ref MPTNode node, byte[] path, Dictionary if (hashNode.IsEmptyNode) break; node = Resolve(hashNode.Hash); dict.Add(node.GetHash(), node.Encode()); - getProof(ref node, path, dict); + GetProof(ref node, path, dict); break; } case FullNode fullNode: @@ -345,11 +345,11 @@ private void getProof(ref MPTNode node, byte[] path, Dictionary dict.Add(fullNode.GetHash(), fullNode.Encode()); if (path.Length == 0) { - getProof(ref fullNode.Children[16], path, dict); + GetProof(ref fullNode.Children[16], path, dict); } else { - getProof(ref fullNode.Children[path[0]], path.Skip(1), dict); + GetProof(ref fullNode.Children[path[0]], path.Skip(1), dict); } break; } @@ -359,7 +359,7 @@ private void getProof(ref MPTNode node, byte[] path, Dictionary if (prefix.Length == shortNode.Key.Length) { dict.Add(shortNode.GetHash(), shortNode.Encode()); - getProof(ref shortNode.Next, path.Skip(prefix.Length), dict); + GetProof(ref shortNode.Next, path.Skip(prefix.Length), dict); } break; } From bd2db12c81d3fc879246d5ab3b8076a1c13c1022 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 12 Feb 2020 14:04:11 +0800 Subject: [PATCH 032/171] fix some --- src/neo/Trie/MPT/MPTDatabase.cs | 2 +- src/neo/Trie/MPT/MPTTrie.cs | 1 - src/neo/Trie/Trie.cs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index bc9615c132..35ed7ff481 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -12,7 +12,7 @@ public class MPTDatabase public static readonly byte[] Prefix = Encoding.ASCII.GetBytes("MPT"); - public static readonly byte[] ROOT_KEY = Encoding.ASCII.GetBytes("mpt_root"); + public static readonly byte[] ROOT_KEY = Encoding.ASCII.GetBytes("MPT_ROOT"); private byte[] StoreKey(byte[] hash) { diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index fecc245c1d..29513a7da5 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -336,7 +336,6 @@ private void GetProof(ref MPTNode node, byte[] path, Dictionary { if (hashNode.IsEmptyNode) break; node = Resolve(hashNode.Hash); - dict.Add(node.GetHash(), node.Encode()); GetProof(ref node, path, dict); break; } diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/Trie.cs index 51aa65df90..9caf1a07e4 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/Trie.cs @@ -7,7 +7,7 @@ public interface ITrie { bool TryGet(byte[] path, out byte[] value); - bool TryPut(byte[] path, byte[] value); + bool Put(byte[] path, byte[] value); bool TryDelete(byte[] path); From 0b462734bc398c33fe1ec9db24e9e223500b6f03 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 12 Feb 2020 14:06:31 +0800 Subject: [PATCH 033/171] fix ut --- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 54 +++++++++++----------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 9538040cf3..5d1465bf55 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -39,6 +39,8 @@ public void TestInit() var l3 = new ShortNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); + var v3 = new ValueNode(); + v3.Value = Encoding.ASCII.GetBytes("hello"); r.Next = b; b.Children[0] = l1; @@ -48,7 +50,8 @@ public void TestInit() b.Children[10] = l3; root = r; var store = new MemoryStore(); - this.mptdb = new MPTDatabase(store.GetSnapshot()); + var snapshot = store.GetSnapshot(); + this.mptdb = new MPTDatabase(snapshot); mptdb.PutRoot(root.GetHash()); mptdb.Put(r); mptdb.Put(b); @@ -57,6 +60,9 @@ public void TestInit() mptdb.Put(l3); mptdb.Put(v1); mptdb.Put(v2); + mptdb.Put(v3); + snapshot.Commit(); + this.mptdb = new MPTDatabase(store.GetSnapshot()); } [TestMethod] @@ -64,38 +70,34 @@ public void TestTryGet() { var mpt = new MPTTrie(mptdb); - var result = mpt.TryGet("0a0c0001".HexToBytes(), out byte[] value); + var result = mpt.TryGet("ac01".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.AreEqual("abcd", value.ToHexString()); - result = mpt.TryGet("0a0c0909".HexToBytes(), out value); + result = mpt.TryGet("ac99".HexToBytes(), out value); Assert.IsTrue(result); Assert.AreEqual("2222", value.ToHexString()); - result = mpt.TryGet("0a0b0909".HexToBytes(), out value); + result = mpt.TryGet("ab99".HexToBytes(), out value); Assert.IsFalse(result); - result = mpt.TryGet("0a0c0309".HexToBytes(), out value); + result = mpt.TryGet("ac39".HexToBytes(), out value); Assert.IsFalse(result); - result = mpt.TryGet("0a0c0002".HexToBytes(), out value); + result = mpt.TryGet("ac02".HexToBytes(), out value); Assert.IsFalse(result); - result = mpt.TryGet("0a0c090901".HexToBytes(), out value); + result = mpt.TryGet("ac9910".HexToBytes(), out value); Assert.AreEqual(false, result); } [TestMethod] public void TestTryGetResolve() { - var n = new ValueNode(); - n.Value = Encoding.ASCII.GetBytes("hello"); - mptdb.Put(n); var mpt = new MPTTrie(mptdb); - var result = mpt.TryGet("0a0c0a0e".HexToBytes(), out byte[] value); - + var result = mpt.TryGet("acae".HexToBytes(), out byte[] value); Assert.IsTrue(result); - Assert.IsTrue(value.Equal(n.Value)); + Assert.IsTrue(Encoding.ASCII.GetBytes("hello").Equal(value)); } [TestMethod] @@ -106,9 +108,9 @@ public void TestTryPut() var mpt1 = new MPTTrie(mptdb); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt1.GetRoot().ToHexString()); var mpt = new MPTTrie(db); - mpt.TryPut("0a0c0001".HexToBytes(), "abcd".HexToBytes()); - mpt.TryPut("0a0c0909".HexToBytes(), "2222".HexToBytes()); - mpt.TryPut("0a0c0a0e".HexToBytes(), Encoding.ASCII.GetBytes("hello")); + mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); + mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); + mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); } @@ -118,7 +120,6 @@ public void TestTryDelete() var r1 = new ShortNode(); r1.Key = "0a0c0001".HexToBytes(); - var r = new ShortNode(); r.Key = "0a0c".HexToBytes(); @@ -146,12 +147,12 @@ public void TestTryDelete() var mpt = new MPTTrie(mptdb); var result = true; - result = mpt.TryGet("0a0c0909".HexToBytes(), out byte[] value); + result = mpt.TryGet("ac99".HexToBytes(), out byte[] value); Assert.IsTrue(result); - result = mpt.TryDelete("0a0c0909".HexToBytes()); + result = mpt.TryDelete("ac99".HexToBytes()); + Assert.IsTrue(result); + result = mpt.TryDelete("acae".HexToBytes()); Assert.IsTrue(result); - result = mpt.TryDelete("0a0c0a0e".HexToBytes()); - Assert.IsFalse(result); Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", mpt.GetRoot().ToHexString()); } @@ -174,6 +175,8 @@ public void TestGetProof() var l3 = new ShortNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); + var v3 = new ValueNode(); + v3.Value = Encoding.ASCII.GetBytes("hello"); r.Next = b; b.Children[0] = l1; @@ -184,13 +187,10 @@ public void TestGetProof() var mpt = new MPTTrie(mptdb); - Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); - var dict = mpt.GetProof("0a0c0001".HexToBytes()); + Assert.AreEqual(r.GetHash().ToHexString(), mpt.GetRoot().ToHexString()); + var dict = mpt.GetProof("ac01".HexToBytes()); Assert.AreEqual(4, dict.Count); - Assert.IsTrue(dict.TryGetValue(mpt.GetRoot(), out byte[] value)); - Assert.IsTrue(dict.TryGetValue(b.GetHash(), out value)); - Assert.IsTrue(dict.TryGetValue(l1.GetHash(), out value)); - Assert.IsTrue(dict.TryGetValue(v1.GetHash(), out value)); + Assert.IsTrue(dict.TryGetValue(mpt.GetRoot(), out byte[] value)); } } } From e261c0248b5aec3441560faca645c7c7aa8f4d8c Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 12 Feb 2020 16:40:37 +0800 Subject: [PATCH 034/171] format --- src/neo/Trie/MPT/MPTDatabase.cs | 1 - src/neo/Trie/MPT/MPTNode.cs | 4 ++-- src/neo/Trie/MPT/MPTNode/FullNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 2 +- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 6 ++---- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 35ed7ff481..46a2eacc15 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -1,4 +1,3 @@ - using Neo.Persistence; using System.Text; diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index a144a9708f..56c0958be5 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -1,7 +1,7 @@ -using System.IO; -using System.Text; using Neo.Cryptography; using Neo.IO; +using System.IO; +using System.Text; namespace Neo.Trie.MPT { diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 6c2d566ff4..aef0016ebf 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -1,6 +1,6 @@ -using System.IO; using Neo.Cryptography; using Neo.IO; +using System.IO; namespace Neo.Trie.MPT { diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index b8fa035207..97ee0ee52b 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -1,6 +1,6 @@ -using System.IO; using Neo.Cryptography; using Neo.IO; +using System.IO; namespace Neo.Trie.MPT { diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index 0b9f6c96ee..8507740539 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -1,6 +1,6 @@ -using System.IO; using Neo.Cryptography; using Neo.IO; +using System.IO; namespace Neo.Trie.MPT { diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 5d1465bf55..dc120df8a0 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -1,9 +1,7 @@ -using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Trie.MPT; using Neo.Persistence; -using Neo.IO; -using System; +using Neo.Trie.MPT; +using System.Text; namespace Neo.UnitTests.Trie.MPT { From c2043c20db330a67ce4623498c06eb281587afbe Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 13 Feb 2020 14:41:11 +0800 Subject: [PATCH 035/171] fix size and exception --- src/neo/Trie/MPT/MPTNode.cs | 7 +++++-- src/neo/Trie/MPT/MPTNode/FullNode.cs | 10 ++++++++++ src/neo/Trie/MPT/MPTNode/HashNode.cs | 1 + src/neo/Trie/MPT/MPTNode/ShortNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 1 + src/neo/Trie/MPT/MPTTrie.cs | 4 ++-- 6 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 56c0958be5..77455b1c8b 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -44,7 +44,7 @@ public void ResetFlag() Flag = new NodeFlag(); } - public int Size { get; } + public virtual int Size { get; } public MPTNode() { @@ -68,6 +68,9 @@ public byte[] Encode() public static MPTNode Decode(byte[] data) { + if (data is null || data.Length == 0 ) + throw new System.ArgumentException(); + var nodeType = (NodeType)data[0]; data = data.Skip(1); @@ -95,7 +98,7 @@ public static MPTNode Decode(byte[] data) return n; } default: - throw new System.Exception(); + throw new System.InvalidOperationException(); } } } diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index aef0016ebf..8b39ddf169 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -10,6 +10,16 @@ public class FullNode : MPTNode public MPTNode[] Children = new MPTNode[CHILD_COUNT]; + public override int Size { + get { + var size = 1; + for (int i = 0; i < Children.Length; i++) + { + size += Children[i].GetHash().Length; + } + return size; + } + } public FullNode() { nType = NodeType.FullNode; diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index 21ac37b11f..f8aec76c20 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -5,6 +5,7 @@ public class HashNode : MPTNode { public byte[] Hash; + public override int Size => 1 + Hash.Length; public HashNode() { nType = NodeType.HashNode; diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 97ee0ee52b..4bd1977864 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -10,7 +10,7 @@ public class ShortNode : MPTNode public MPTNode Next; - public new int Size => Key.Length + Next.Size; + public override int Size => 1 + Key.Length + Next.GetHash().Length; protected override byte[] CalHash() { diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index 8507740539..8db3bb714b 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -8,6 +8,7 @@ public class ValueNode : MPTNode { public byte[] Value; + public override int Size => 1 + Value.Length; protected override byte[] CalHash() { return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 29513a7da5..38269271ba 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -10,7 +10,7 @@ public class MPTTrie : ITrie public MPTTrie(MPTDatabase db) { if (db is null) - throw new System.Exception(); + throw new System.ArgumentNullException(); this.db = db; var rbytes = db.GetRoot(); if (rbytes is null || rbytes.Length == 0) @@ -192,7 +192,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) return Put(ref node, path, val); } default: - throw new System.Exception(); + throw new System.InvalidOperationException("Invalid node type."); } } From 8c8ee91daa7691a4a38098c3b669683651b00dec Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 13 Feb 2020 14:43:53 +0800 Subject: [PATCH 036/171] format --- src/neo/Trie/MPT/MPTNode.cs | 4 ++-- src/neo/Trie/MPT/MPTNode/FullNode.cs | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 77455b1c8b..833dfa3b2e 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -68,9 +68,9 @@ public byte[] Encode() public static MPTNode Decode(byte[] data) { - if (data is null || data.Length == 0 ) + if (data is null || data.Length == 0) throw new System.ArgumentException(); - + var nodeType = (NodeType)data[0]; data = data.Skip(1); diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 8b39ddf169..0b92f4bef6 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -7,11 +7,13 @@ namespace Neo.Trie.MPT public class FullNode : MPTNode { public const int CHILD_COUNT = 17; - + public MPTNode[] Children = new MPTNode[CHILD_COUNT]; - public override int Size { - get { + public override int Size + { + get + { var size = 1; for (int i = 0; i < Children.Length; i++) { From 8342cb114abfc13d79b250e33bd05ebfcc2ea8fc Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 14 Feb 2020 15:01:41 +0800 Subject: [PATCH 037/171] format --- src/neo/Trie/MPT/MPTNode/FullNode.cs | 1 + src/neo/Trie/MPT/MPTNode/HashNode.cs | 1 + src/neo/Trie/MPT/MPTNode/ShortNode.cs | 1 + src/neo/Trie/MPT/MPTNode/ValueNode.cs | 1 + 4 files changed, 4 insertions(+) diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 0b92f4bef6..6a3dccfedb 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -22,6 +22,7 @@ public override int Size return size; } } + public FullNode() { nType = NodeType.FullNode; diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index f8aec76c20..7ff8f6b0c6 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -6,6 +6,7 @@ public class HashNode : MPTNode public byte[] Hash; public override int Size => 1 + Hash.Length; + public HashNode() { nType = NodeType.HashNode; diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 4bd1977864..4004f73296 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -16,6 +16,7 @@ protected override byte[] CalHash() { return Key.Concat(Next.GetHash()).Sha256(); } + public ShortNode() { nType = NodeType.ShortNode; diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index 8db3bb714b..b767966e9e 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -9,6 +9,7 @@ public class ValueNode : MPTNode public byte[] Value; public override int Size => 1 + Value.Length; + protected override byte[] CalHash() { return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); From 7f5314a94ad50ed2f62cef55c2f8b62fb9c72857 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 14 Feb 2020 16:07:27 +0800 Subject: [PATCH 038/171] rm Serialize and Deserialize use Encode and Decode --- src/neo/Trie/MPT/MPTNode.cs | 37 ++++++++++++--------------- src/neo/Trie/MPT/MPTNode/FullNode.cs | 25 +++++------------- src/neo/Trie/MPT/MPTNode/HashNode.cs | 17 +++++++++--- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 15 +++++------ src/neo/Trie/MPT/MPTNode/ValueNode.cs | 7 ++--- 5 files changed, 47 insertions(+), 54 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 833dfa3b2e..42cd6ce98b 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -24,7 +24,7 @@ public NodeFlag() } } - public abstract class MPTNode : ISerializable + public abstract class MPTNode { public NodeFlag Flag; protected NodeType nType; @@ -44,57 +44,52 @@ public void ResetFlag() Flag = new NodeFlag(); } - public virtual int Size { get; } - public MPTNode() { Flag = new NodeFlag(); } - public virtual void Serialize(BinaryWriter writer) - { - writer.Write((byte)nType); - } - - public virtual void Deserialize(BinaryReader reader) - { - - } - public byte[] Encode() { - return this.ToArray(); + using (MemoryStream ms = new MemoryStream()) + using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) + { + writer.Write((byte)nType); + EncodeSpecific(writer); + writer.Flush(); + return ms.ToArray(); + } } + public abstract void EncodeSpecific(BinaryWriter writer); + public static MPTNode Decode(byte[] data) { if (data is null || data.Length == 0) throw new System.ArgumentException(); - var nodeType = (NodeType)data[0]; - data = data.Skip(1); - using (MemoryStream ms = new MemoryStream(data, false)) using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) { + var nodeType = (NodeType)reader.ReadByte(); switch (nodeType) { case NodeType.FullNode: { var n = new FullNode(); - n.Deserialize(reader); + n.DecodeSpecific(reader); return n; } case NodeType.ShortNode: { var n = new ShortNode(); - n.Deserialize(reader); + n.DecodeSpecific(reader); return n; } case NodeType.ValueNode: { var n = new ValueNode(); - n.Deserialize(reader); + n.DecodeSpecific(reader); return n; } default: @@ -102,5 +97,7 @@ public static MPTNode Decode(byte[] data) } } } + + public abstract void DecodeSpecific(BinaryReader reader); } } diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 6a3dccfedb..2fd1d603bc 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -10,19 +10,7 @@ public class FullNode : MPTNode public MPTNode[] Children = new MPTNode[CHILD_COUNT]; - public override int Size - { - get - { - var size = 1; - for (int i = 0; i < Children.Length; i++) - { - size += Children[i].GetHash().Length; - } - return size; - } - } - + public FullNode() { nType = NodeType.FullNode; @@ -42,20 +30,21 @@ protected override byte[] CalHash() return bytes.Sha256(); } - public override void Serialize(BinaryWriter writer) + public override void EncodeSpecific(BinaryWriter writer) { - base.Serialize(writer); for (int i = 0; i < Children.Length; i++) { - writer.WriteVarBytes(Children[i].GetHash()); + var hashNode = new HashNode(Children[i].GetHash()); + hashNode.EncodeSpecific(writer); } } - public override void Deserialize(BinaryReader reader) + public override void DecodeSpecific(BinaryReader reader) { for (int i = 0; i < Children.Length; i++) { - var hashNode = new HashNode(reader.ReadVarBytes()); + var hashNode = new HashNode(); + hashNode.DecodeSpecific(reader); Children[i] = hashNode; } } diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index 7ff8f6b0c6..dc2f9277a8 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -1,3 +1,5 @@ +using Neo.IO; +using System.IO; namespace Neo.Trie.MPT { @@ -5,8 +7,6 @@ public class HashNode : MPTNode { public byte[] Hash; - public override int Size => 1 + Hash.Length; - public HashNode() { nType = NodeType.HashNode; @@ -20,6 +20,7 @@ public HashNode(byte[] hash) protected override byte[] CalHash() { + if (IsEmptyNode) return new byte[]{}; return (byte[])Hash.Clone(); } @@ -28,6 +29,16 @@ public static HashNode EmptyNode() return new HashNode(new byte[] { }); } - public bool IsEmptyNode => Hash.Length == 0; + public bool IsEmptyNode => Hash is null || Hash.Length == 0; + + public override void EncodeSpecific(BinaryWriter writer) + { + writer.WriteVarBytes(Hash); + } + + public override void DecodeSpecific(BinaryReader reader) + { + Hash = reader.ReadVarBytes(); + } } } diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 4004f73296..89aa4f5a1e 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -10,29 +10,28 @@ public class ShortNode : MPTNode public MPTNode Next; - public override int Size => 1 + Key.Length + Next.GetHash().Length; - protected override byte[] CalHash() { return Key.Concat(Next.GetHash()).Sha256(); } - + public ShortNode() { nType = NodeType.ShortNode; } - public override void Serialize(BinaryWriter writer) + public override void EncodeSpecific(BinaryWriter writer) { - base.Serialize(writer); writer.WriteVarBytes(Key); - writer.WriteVarBytes(Next.GetHash()); + var hashNode = new HashNode(Next.GetHash()); + hashNode.EncodeSpecific(writer); } - public override void Deserialize(BinaryReader reader) + public override void DecodeSpecific(BinaryReader reader) { Key = reader.ReadVarBytes(); - var hashNode = new HashNode(reader.ReadVarBytes()); + var hashNode = new HashNode(); + hashNode.DecodeSpecific(reader); Next = hashNode; } } diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index b767966e9e..45ec5e9b97 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -8,8 +8,6 @@ public class ValueNode : MPTNode { public byte[] Value; - public override int Size => 1 + Value.Length; - protected override byte[] CalHash() { return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); @@ -26,13 +24,12 @@ public ValueNode(byte[] val) Value = (byte[])val.Clone(); } - public override void Serialize(BinaryWriter writer) + public override void EncodeSpecific(BinaryWriter writer) { - base.Serialize(writer); writer.WriteVarBytes(Value); } - public override void Deserialize(BinaryReader reader) + public override void DecodeSpecific(BinaryReader reader) { Value = reader.ReadVarBytes(); } From 7d015107a4105724c40c8b6f902c061a36d7d7e6 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 14 Feb 2020 16:10:55 +0800 Subject: [PATCH 039/171] format --- src/neo/Trie/MPT/MPTDatabase.cs | 3 --- src/neo/Trie/MPT/MPTNode.cs | 1 - src/neo/Trie/MPT/MPTNode/FullNode.cs | 1 - src/neo/Trie/MPT/MPTNode/ShortNode.cs | 1 - 4 files changed, 6 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 46a2eacc15..f08b4c96fb 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -6,11 +6,8 @@ namespace Neo.Trie.MPT public class MPTDatabase { private ISnapshot store; - public static readonly byte TABLE = 0x4D; - public static readonly byte[] Prefix = Encoding.ASCII.GetBytes("MPT"); - public static readonly byte[] ROOT_KEY = Encoding.ASCII.GetBytes("MPT_ROOT"); private byte[] StoreKey(byte[] hash) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 42cd6ce98b..af16812a21 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -28,7 +28,6 @@ public abstract class MPTNode { public NodeFlag Flag; protected NodeType nType; - protected abstract byte[] CalHash(); public virtual byte[] GetHash() diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 2fd1d603bc..ab1be4a451 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -7,7 +7,6 @@ namespace Neo.Trie.MPT public class FullNode : MPTNode { public const int CHILD_COUNT = 17; - public MPTNode[] Children = new MPTNode[CHILD_COUNT]; diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 89aa4f5a1e..c8910269d8 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -7,7 +7,6 @@ namespace Neo.Trie.MPT public class ShortNode : MPTNode { public byte[] Key; - public MPTNode Next; protected override byte[] CalHash() From 0321ab62f99b6753bdf9862c3ffea5a3e83640dc Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 14 Feb 2020 16:36:32 +0800 Subject: [PATCH 040/171] format --- tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 6 +++--- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs index c54213848f..ba9f11761c 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -9,8 +9,8 @@ public class UT_Helper [TestMethod] public void TestConcat() { - var a = new byte[]{0x01}; - var b = new byte[]{0x02}; + var a = new byte[] { 0x01 }; + var b = new byte[] { 0x02 }; a = a.Concat(b); Assert.AreEqual(2, a.Length); } @@ -36,7 +36,7 @@ public void TestSkip() s = s.Skip(2); Assert.AreEqual("01", s.ToHexString()); - s = new byte[]{0x01}; + s = new byte[] { 0x01 }; s = s.Skip(1); Assert.AreEqual(0, s.Length); s = s.Skip(2); diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index dc120df8a0..b2605deb90 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -25,9 +25,9 @@ public void TestInit() r.Key = "0a0c".HexToBytes(); var b = new FullNode(); var l1 = new ShortNode(); - l1.Key = new byte[]{0x01}; + l1.Key = new byte[] { 0x01 }; var l2 = new ShortNode(); - l2.Key = new byte[]{0x09}; + l2.Key = new byte[] { 0x09 }; var v1 = new ValueNode(); v1.Value = "abcd".HexToBytes(); var v2 = new ValueNode(); @@ -126,14 +126,14 @@ public void TestTryDelete() r.Next = b; var l1 = new ShortNode(); - l1.Key = new byte[]{0x01}; + l1.Key = new byte[] { 0x01 }; var v1 = new ValueNode(); v1.Value = "abcd".HexToBytes(); l1.Next = v1; b.Children[0] = l1; var l2 = new ShortNode(); - l2.Key = new byte[]{0x09}; + l2.Key = new byte[] { 0x09 }; var v2 = new ValueNode(); v2.Value = "2222".HexToBytes(); l2.Next = v2; @@ -161,9 +161,9 @@ public void TestGetProof() r.Key = "0a0c".HexToBytes(); var b = new FullNode(); var l1 = new ShortNode(); - l1.Key = new byte[]{0x01}; + l1.Key = new byte[] { 0x01 }; var l2 = new ShortNode(); - l2.Key = new byte[]{0x09}; + l2.Key = new byte[] { 0x09 }; var v1 = new ValueNode(); v1.Value = "abcd".HexToBytes(); var v2 = new ValueNode(); From c87dfeae7a4349a076890b5cc6ba170370cc7265 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 14 Feb 2020 16:41:02 +0800 Subject: [PATCH 041/171] format --- src/neo/Trie/MPT/MPTNode/HashNode.cs | 2 +- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 15 ++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index dc2f9277a8..8b8a128483 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -20,7 +20,7 @@ public HashNode(byte[] hash) protected override byte[] CalHash() { - if (IsEmptyNode) return new byte[]{}; + if (IsEmptyNode) return new byte[] { }; return (byte[])Hash.Clone(); } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index b2605deb90..e3c98745fc 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -9,14 +9,7 @@ namespace Neo.UnitTests.Trie.MPT public class UT_MPTTrie { private MPTNode root; - private MPTDatabase mptdb; - - [ClassInitialize] - public static void ClassInit(TestContext context) - { - - } [TestInitialize] public void TestInit() @@ -66,7 +59,7 @@ public void TestInit() [TestMethod] public void TestTryGet() { - + var mpt = new MPTTrie(mptdb); var result = mpt.TryGet("ac01".HexToBytes(), out byte[] value); Assert.IsTrue(result); @@ -117,7 +110,7 @@ public void TestTryDelete() { var r1 = new ShortNode(); r1.Key = "0a0c0001".HexToBytes(); - + var r = new ShortNode(); r.Key = "0a0c".HexToBytes(); @@ -138,7 +131,7 @@ public void TestTryDelete() v2.Value = "2222".HexToBytes(); l2.Next = v2; b.Children[9] = l2; - + r1.Next = v1; Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", r1.GetHash().ToHexString()); Assert.AreEqual("f3ad94e8fb6e1e85a8b573b2343845e3b0e0b96b61fcd0e20b6df159fde137a7", r.GetHash().ToHexString()); @@ -188,7 +181,7 @@ public void TestGetProof() Assert.AreEqual(r.GetHash().ToHexString(), mpt.GetRoot().ToHexString()); var dict = mpt.GetProof("ac01".HexToBytes()); Assert.AreEqual(4, dict.Count); - Assert.IsTrue(dict.TryGetValue(mpt.GetRoot(), out byte[] value)); + Assert.IsTrue(dict.TryGetValue(mpt.GetRoot(), out byte[] value)); } } } From f5c8df41910bd7f58e8fbcef0b3c981f95716cd7 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 17 Feb 2020 09:37:55 +0800 Subject: [PATCH 042/171] rm prefix --- src/neo/Trie/MPT/MPTDatabase.cs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index f08b4c96fb..3a65a7f664 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -7,14 +7,8 @@ public class MPTDatabase { private ISnapshot store; public static readonly byte TABLE = 0x4D; - public static readonly byte[] Prefix = Encoding.ASCII.GetBytes("MPT"); public static readonly byte[] ROOT_KEY = Encoding.ASCII.GetBytes("MPT_ROOT"); - private byte[] StoreKey(byte[] hash) - { - return Prefix.Concat(hash); - } - public MPTDatabase(ISnapshot store) { this.store = store; @@ -22,29 +16,29 @@ public MPTDatabase(ISnapshot store) public MPTNode Node(byte[] hash) { - var data = store.TryGet(TABLE, StoreKey(hash)); + var data = store.TryGet(TABLE, hash); var n = MPTNode.Decode(data); return n; } public void Delete(byte[] hash) { - store.Delete(TABLE, StoreKey(hash)); + store.Delete(TABLE, hash); } public void Put(MPTNode node) { - store.Put(TABLE, StoreKey(node.GetHash()), node.Encode()); + store.Put(TABLE, node.GetHash(), node.Encode()); } public void PutRoot(byte[] root) { - store.Put(TABLE, StoreKey(ROOT_KEY), root); + store.Put(TABLE, ROOT_KEY, root); } public byte[] GetRoot() { - return store.TryGet(TABLE, StoreKey(ROOT_KEY)); + return store.TryGet(TABLE, ROOT_KEY); } } } From 6cd152e20b3cc580fd9b852489abe7a72f784a48 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 17 Feb 2020 09:39:30 +0800 Subject: [PATCH 043/171] assign enum value --- src/neo/Trie/MPT/MPTNode.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index af16812a21..d0d212e0b7 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -7,10 +7,10 @@ namespace Neo.Trie.MPT { public enum NodeType { - FullNode, - ShortNode, - HashNode, - ValueNode, + FullNode = 0x00, + ShortNode = 0x01, + HashNode = 0x02, + ValueNode = 0x03, } public class NodeFlag From 978c52a733dd69506a1aeff91f64bcf33389b315 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 20 Feb 2020 09:44:05 +0800 Subject: [PATCH 044/171] fix some --- src/neo/Trie/MPT/MPTTrie.cs | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 38269271ba..eefc6e9e33 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -11,7 +11,9 @@ public MPTTrie(MPTDatabase db) { if (db is null) throw new System.ArgumentNullException(); + this.db = db; + var rbytes = db.GetRoot(); if (rbytes is null || rbytes.Length == 0) { @@ -77,12 +79,12 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) public bool Put(byte[] path, byte[] value) { - var n = new ValueNode(value); path = path.ToNibbles(); if (value.Length == 0) { return TryDelete(ref root, path); } + var n = new ValueNode(value); return Put(ref root, path, n); } @@ -92,10 +94,10 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { case ValueNode valueNode: { - if (path.Length == 0 && val is ValueNode) + if (path.Length == 0 && val is ValueNode v) { db.Delete(node.GetHash()); - node = val; + valueNode = v; db.Put(valueNode); return true; } @@ -123,20 +125,18 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) MPTNode grandSon1 = HashNode.EmptyNode(), grandSon2 = HashNode.EmptyNode(); Put(ref grandSon1, keyRemain.Skip(1), shortNode.Next); - db.Put(grandSon1); son.Children[keyRemain[0]] = grandSon1; if (pathRemain.Length == 0) { Put(ref grandSon2, pathRemain, val); - son.Children[son.Children.Length] = grandSon2; + son.Children[FullNode.CHILD_COUNT - 1] = grandSon2; } else { Put(ref grandSon2, pathRemain.Skip(1), val); son.Children[pathRemain[0]] = grandSon2; } - db.Put(grandSon2); db.Put(son); if (0 < prefix.Length) { @@ -146,7 +146,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) Next = son, }; db.Put(extensionNode); - node = extensionNode; + shortNode = extensionNode; } else { @@ -161,7 +161,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) var oldHash = fullNode.GetHash(); if (path.Length == 0) { - result = Put(ref fullNode.Children[fullNode.Children.Length], path, val); + result = Put(ref fullNode.Children[FullNode.CHILD_COUNT - 1], path, val); } else { @@ -228,19 +228,16 @@ private bool TryDelete(ref MPTNode node, byte[] path) if (shortNode.Next is HashNode hashNode && hashNode.IsEmptyNode) { node = shortNode.Next; + return true; } - else if (shortNode.Next is ShortNode sn) + if (shortNode.Next is ShortNode sn) { shortNode.Key = shortNode.Key.Concat(sn.Key); shortNode.Next = sn.Next; - shortNode.ResetFlag(); - db.Put(shortNode); - } - else - { - node.ResetFlag(); - db.Put(shortNode); + db.Delete(sn.GetHash()); } + shortNode.ResetFlag(); + db.Put(shortNode); return true; } return false; @@ -251,7 +248,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) var oldHash = fullNode.GetHash(); if (path.Length == 0) { - result = TryDelete(ref fullNode.Children[fullNode.Children.Length], path); + result = TryDelete(ref fullNode.Children[FullNode.CHILD_COUNT - 1], path); } else { @@ -260,7 +257,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) if (!result) return false; db.Delete(oldHash); var nonEmptyChildren = new byte[] { }; - for (int i = 0; i < fullNode.Children.Length; i++) + for (int i = 0; i < FullNode.CHILD_COUNT; i++) { if (fullNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; nonEmptyChildren = nonEmptyChildren.Add((byte)i); From e8afc1ac9f6845f646823681a3fe6cfbe882a1b0 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 20 Feb 2020 09:46:50 +0800 Subject: [PATCH 045/171] format --- src/neo/Trie/MPT/MPTTrie.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index eefc6e9e33..16eb6c2d09 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -122,7 +122,8 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) var pathRemain = path.Skip(prefix.Length); var keyRemain = shortNode.Key.Skip(prefix.Length); var son = new FullNode(); - MPTNode grandSon1 = HashNode.EmptyNode(), grandSon2 = HashNode.EmptyNode(); + MPTNode grandSon1 = HashNode.EmptyNode(); + MPTNode grandSon2 = HashNode.EmptyNode(); Put(ref grandSon1, keyRemain.Skip(1), shortNode.Next); son.Children[keyRemain[0]] = grandSon1; From af83056deacc482a24784dcfa0957ecf5050afcf Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 20 Feb 2020 09:55:39 +0800 Subject: [PATCH 046/171] empty byte array --- src/neo/Trie/MPT/Helper.cs | 4 ++-- src/neo/Trie/MPT/MPTNode/HashNode.cs | 5 +++-- src/neo/Trie/MPT/MPTTrie.cs | 5 +++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs index 484cdf7128..81a0fa365c 100644 --- a/src/neo/Trie/MPT/Helper.cs +++ b/src/neo/Trie/MPT/Helper.cs @@ -14,7 +14,7 @@ public static byte[] Concat(this byte[] a, byte[] b) public static byte[] CommonPrefix(this byte[] a, byte[] b) { - var prefix = new byte[] { }; + var prefix = Array.Empty(); var minLen = a.Length <= b.Length ? a.Length : b.Length; if (a.Length != 0 && b.Length != 0) @@ -40,7 +40,7 @@ public static bool Equal(this byte[] a, byte[] b) public static byte[] Skip(this byte[] a, int count) { - var result = new byte[] { }; + var result = Array.Empty(); var len = a.Length - count; if (0 < len) { diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index 8b8a128483..d5e46d2a84 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -1,4 +1,5 @@ using Neo.IO; +using System; using System.IO; namespace Neo.Trie.MPT @@ -20,13 +21,13 @@ public HashNode(byte[] hash) protected override byte[] CalHash() { - if (IsEmptyNode) return new byte[] { }; + if (IsEmptyNode) return Array.Empty(); return (byte[])Hash.Clone(); } public static HashNode EmptyNode() { - return new HashNode(new byte[] { }); + return new HashNode(Array.Empty()); } public bool IsEmptyNode => Hash is null || Hash.Length == 0; diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 16eb6c2d09..7919e00d24 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; namespace Neo.Trie.MPT @@ -73,7 +74,7 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) break; } } - value = new byte[] { }; + value = Array.Empty(); return false; } @@ -257,7 +258,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) } if (!result) return false; db.Delete(oldHash); - var nonEmptyChildren = new byte[] { }; + var nonEmptyChildren = Array.Empty(); for (int i = 0; i < FullNode.CHILD_COUNT; i++) { if (fullNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; From 069eedef652250c559bb890cd863e7de7fa50884 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 20 Feb 2020 10:09:10 +0800 Subject: [PATCH 047/171] check if change when commit --- src/neo/Trie/MPT/MPTTrie.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 7919e00d24..876ff939f7 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -366,7 +366,10 @@ private void GetProof(ref MPTNode node, byte[] path, Dictionary public void Commit() { - db.PutRoot(GetRoot()); + if (root.Flag.Dirty) + { + db.PutRoot(GetRoot()); + } } } } From bb89eac8daa92b8766f7d0fbb87f08e12953f4da Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 20 Feb 2020 10:14:26 +0800 Subject: [PATCH 048/171] unit test --- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index e3c98745fc..69ccaa7d03 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -102,7 +102,7 @@ public void TestTryPut() mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); - Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt.GetRoot().ToHexString()); + Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", mpt.GetRoot().ToHexString()); } [TestMethod] From d26c1492494a53f249969efb3d0e22f24c3a8198 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 21 Feb 2020 16:10:22 +0800 Subject: [PATCH 049/171] format --- src/neo/Trie/MPT/MPTTrie.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 876ff939f7..1a2b02e5a7 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -140,7 +140,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) son.Children[pathRemain[0]] = grandSon2; } db.Put(son); - if (0 < prefix.Length) + if (prefix.Length > 0) { var extensionNode = new ShortNode() { From 79f98ce74df62b2879cf0377c6f3932696da40f3 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 21 Feb 2020 16:12:42 +0800 Subject: [PATCH 050/171] change nonEmptyChildren to childrenIndexes --- src/neo/Trie/MPT/MPTTrie.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 1a2b02e5a7..5b19c0532b 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -258,26 +258,26 @@ private bool TryDelete(ref MPTNode node, byte[] path) } if (!result) return false; db.Delete(oldHash); - var nonEmptyChildren = Array.Empty(); + var childrenIndexes = Array.Empty(); for (int i = 0; i < FullNode.CHILD_COUNT; i++) { if (fullNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; - nonEmptyChildren = nonEmptyChildren.Add((byte)i); + childrenIndexes = childrenIndexes.Add((byte)i); } - if (1 < nonEmptyChildren.Length) + if (childrenIndexes.Length > 1) { fullNode.ResetFlag(); db.Put(fullNode); return true; } - var childIndex = nonEmptyChildren[0]; + var childIndex = childrenIndexes[0]; var child = fullNode.Children[childIndex]; if (child is HashNode hashNode) child = Resolve(hashNode.Hash); if (child is ShortNode shortNode) { db.Delete(shortNode.GetHash()); - shortNode.Key = nonEmptyChildren.Concat(shortNode.Key); + shortNode.Key = childrenIndexes.Concat(shortNode.Key); shortNode.ResetFlag(); db.Put(shortNode); node = shortNode; @@ -285,7 +285,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) } var newNode = new ShortNode() { - Key = nonEmptyChildren, + Key = childrenIndexes, Next = child, }; node = newNode; From 1d0041cf9a31b30fd1b97b0b5fd9be254825f695 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 21 Feb 2020 16:18:58 +0800 Subject: [PATCH 051/171] change mpttrie initialize and ut --- src/neo/Trie/MPT/MPTTrie.cs | 7 +++--- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 27 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 5b19c0532b..f6c3010f75 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,3 +1,4 @@ +using Neo.Persistence; using System; using System.Collections.Generic; @@ -8,12 +9,12 @@ public class MPTTrie : ITrie private MPTDatabase db; private MPTNode root; - public MPTTrie(MPTDatabase db) + public MPTTrie(ISnapshot store) { - if (db is null) + if (store is null) throw new System.ArgumentNullException(); - this.db = db; + this.db = new MPTDatabase(store); var rbytes = db.GetRoot(); if (rbytes is null || rbytes.Length == 0) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 69ccaa7d03..6813651d25 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -9,7 +9,7 @@ namespace Neo.UnitTests.Trie.MPT public class UT_MPTTrie { private MPTNode root; - private MPTDatabase mptdb; + private ISnapshot mptdb; [TestInitialize] public void TestInit() @@ -42,18 +42,18 @@ public void TestInit() root = r; var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - this.mptdb = new MPTDatabase(snapshot); - mptdb.PutRoot(root.GetHash()); - mptdb.Put(r); - mptdb.Put(b); - mptdb.Put(l1); - mptdb.Put(l2); - mptdb.Put(l3); - mptdb.Put(v1); - mptdb.Put(v2); - mptdb.Put(v3); + var db = new MPTDatabase(snapshot); + db.PutRoot(root.GetHash()); + db.Put(r); + db.Put(b); + db.Put(l1); + db.Put(l2); + db.Put(l3); + db.Put(v1); + db.Put(v2); + db.Put(v3); snapshot.Commit(); - this.mptdb = new MPTDatabase(store.GetSnapshot()); + this.mptdb = store.GetSnapshot(); } [TestMethod] @@ -95,10 +95,9 @@ public void TestTryGetResolve() public void TestTryPut() { var store = new MemoryStore(); - var db = new MPTDatabase(store.GetSnapshot()); var mpt1 = new MPTTrie(mptdb); Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt1.GetRoot().ToHexString()); - var mpt = new MPTTrie(db); + var mpt = new MPTTrie(store.GetSnapshot()); mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); From d415f279e3fc3a08e03b04631c93a1533c27507a Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 21 Feb 2020 16:21:26 +0800 Subject: [PATCH 052/171] rm blanks --- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 6813651d25..23150148e3 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -59,7 +59,6 @@ public void TestInit() [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(mptdb); var result = mpt.TryGet("ac01".HexToBytes(), out byte[] value); Assert.IsTrue(result); @@ -113,7 +112,6 @@ public void TestTryDelete() var r = new ShortNode(); r.Key = "0a0c".HexToBytes(); - var b = new FullNode(); r.Next = b; @@ -176,7 +174,6 @@ public void TestGetProof() b.Children[10] = l3; var mpt = new MPTTrie(mptdb); - Assert.AreEqual(r.GetHash().ToHexString(), mpt.GetRoot().ToHexString()); var dict = mpt.GetProof("ac01".HexToBytes()); Assert.AreEqual(4, dict.Count); From a4f3e22e80a674ec2f447e7bb42bed4df4ae24e6 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 21 Feb 2020 16:22:58 +0800 Subject: [PATCH 053/171] change public to private --- src/neo/Trie/MPT/MPTDatabase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDatabase.cs index 3a65a7f664..314e6854f8 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDatabase.cs @@ -6,8 +6,8 @@ namespace Neo.Trie.MPT public class MPTDatabase { private ISnapshot store; - public static readonly byte TABLE = 0x4D; - public static readonly byte[] ROOT_KEY = Encoding.ASCII.GetBytes("MPT_ROOT"); + private static readonly byte TABLE = 0x4D; + private static readonly byte[] ROOT_KEY = Encoding.ASCII.GetBytes("MPT_ROOT"); public MPTDatabase(ISnapshot store) { From 56bf677ca84008473b59eb2a6576573e50d43fc5 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 21 Feb 2020 16:25:17 +0800 Subject: [PATCH 054/171] calhash to genhash --- src/neo/Trie/MPT/MPTNode.cs | 5 +++-- src/neo/Trie/MPT/MPTNode/FullNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/HashNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index d0d212e0b7..4ec05879b9 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -28,12 +28,13 @@ public abstract class MPTNode { public NodeFlag Flag; protected NodeType nType; - protected abstract byte[] CalHash(); + + protected abstract byte[] GenHash(); public virtual byte[] GetHash() { if (!Flag.Dirty) return Flag.Hash; - Flag.Hash = CalHash(); + Flag.Hash = GenHash(); Flag.Dirty = false; return (byte[])Flag.Hash.Clone(); } diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index ab1be4a451..43e939509b 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -19,7 +19,7 @@ public FullNode() } } - protected override byte[] CalHash() + protected override byte[] GenHash() { var bytes = new byte[0]; for (int i = 0; i < Children.Length; i++) diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index d5e46d2a84..cca61e2882 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -19,7 +19,7 @@ public HashNode(byte[] hash) Hash = (byte[])hash.Clone(); } - protected override byte[] CalHash() + protected override byte[] GenHash() { if (IsEmptyNode) return Array.Empty(); return (byte[])Hash.Clone(); diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index c8910269d8..cf074872ef 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -9,7 +9,7 @@ public class ShortNode : MPTNode public byte[] Key; public MPTNode Next; - protected override byte[] CalHash() + protected override byte[] GenHash() { return Key.Concat(Next.GetHash()).Sha256(); } diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index 45ec5e9b97..ae0d839c86 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -8,7 +8,7 @@ public class ValueNode : MPTNode { public byte[] Value; - protected override byte[] CalHash() + protected override byte[] GenHash() { return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); } From 78293e00c92a1bdf56384e298651dced20fbf9e9 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 21 Feb 2020 16:27:24 +0800 Subject: [PATCH 055/171] change variable name --- src/neo/Trie/MPT/MPTTrie.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index f6c3010f75..d682887e89 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -271,11 +271,11 @@ private bool TryDelete(ref MPTNode node, byte[] path) db.Put(fullNode); return true; } - var childIndex = childrenIndexes[0]; - var child = fullNode.Children[childIndex]; - if (child is HashNode hashNode) - child = Resolve(hashNode.Hash); - if (child is ShortNode shortNode) + var lastChildIndex = childrenIndexes[0]; + var lastChild = fullNode.Children[lastChildIndex]; + if (lastChild is HashNode hashNode) + lastChild = Resolve(hashNode.Hash); + if (lastChild is ShortNode shortNode) { db.Delete(shortNode.GetHash()); shortNode.Key = childrenIndexes.Concat(shortNode.Key); @@ -287,7 +287,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) var newNode = new ShortNode() { Key = childrenIndexes, - Next = child, + Next = lastChild, }; node = newNode; db.Put(node); From 2a1745b6b612ed4f4461a2f51d5ca52ea8956229 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 24 Feb 2020 14:44:59 +0800 Subject: [PATCH 056/171] use Crypto.Hash256 --- src/neo/Trie/MPT/MPTNode/FullNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 2 +- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 43e939509b..3c675a1477 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -26,7 +26,7 @@ protected override byte[] GenHash() { bytes = bytes.Concat(Children[i].GetHash()); } - return bytes.Sha256(); + return Crypto.Hash256(bytes); } public override void EncodeSpecific(BinaryWriter writer) diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index cf074872ef..b07ca2d582 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -11,7 +11,7 @@ public class ShortNode : MPTNode protected override byte[] GenHash() { - return Key.Concat(Next.GetHash()).Sha256(); + return Crypto.Hash256(Key.Concat(Next.GetHash())); } public ShortNode() diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index ae0d839c86..2dc963ca37 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -10,7 +10,7 @@ public class ValueNode : MPTNode protected override byte[] GenHash() { - return Value.Length < 32 ? (byte[])Value.Clone() : Value.Sha256(); + return Value.Length < 32 ? (byte[])Value.Clone() : Crypto.Hash256(Value); } public ValueNode() diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 23150148e3..d980b51ec3 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -95,12 +95,12 @@ public void TestTryPut() { var store = new MemoryStore(); var mpt1 = new MPTTrie(mptdb); - Assert.AreEqual("c32dc0dee8cec33436eff759ee460c65d1a22c0a65a5edd27c68dd80ac3963b4", mpt1.GetRoot().ToHexString()); + Assert.AreEqual("743d2d1f400ae407d14ec19d68c5b6d8791633277a8917d75ab97be4ffed7172", mpt1.GetRoot().ToHexString()); var mpt = new MPTTrie(store.GetSnapshot()); mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); - Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", mpt.GetRoot().ToHexString()); + Assert.AreEqual("aae7f2cd9bcd3b3dadca286ccbecf03d7269fb4cc547fa40ba799abb89c3731f", mpt.GetRoot().ToHexString()); } [TestMethod] @@ -130,8 +130,8 @@ public void TestTryDelete() b.Children[9] = l2; r1.Next = v1; - Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", r1.GetHash().ToHexString()); - Assert.AreEqual("f3ad94e8fb6e1e85a8b573b2343845e3b0e0b96b61fcd0e20b6df159fde137a7", r.GetHash().ToHexString()); + Assert.AreEqual("aae7f2cd9bcd3b3dadca286ccbecf03d7269fb4cc547fa40ba799abb89c3731f", r1.GetHash().ToHexString()); + Assert.AreEqual("a388b72bf6f8af80eed633fe95d7397bf51dcb23ceb2026979bb0b831893368b", r.GetHash().ToHexString()); var mpt = new MPTTrie(mptdb); var result = true; @@ -141,7 +141,7 @@ public void TestTryDelete() Assert.IsTrue(result); result = mpt.TryDelete("acae".HexToBytes()); Assert.IsTrue(result); - Assert.AreEqual("76248d1bf457f0b95c1f6d05d787dca152906f106bcbafacbf7a69c6ae1797c4", mpt.GetRoot().ToHexString()); + Assert.AreEqual("aae7f2cd9bcd3b3dadca286ccbecf03d7269fb4cc547fa40ba799abb89c3731f", mpt.GetRoot().ToHexString()); } [TestMethod] From bb404d4ca8c925879790d45e27febb594dadac49 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 24 Feb 2020 15:04:51 +0800 Subject: [PATCH 057/171] add ready only trie --- src/neo/Trie/IReadOnlyTrie.cs | 15 +++ src/neo/Trie/{Trie.cs => ITrie.cs} | 8 +- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 145 ++++++++++++++++++++++++++++ src/neo/Trie/MPT/MPTTrie.cs | 133 +------------------------ 4 files changed, 163 insertions(+), 138 deletions(-) create mode 100644 src/neo/Trie/IReadOnlyTrie.cs rename src/neo/Trie/{Trie.cs => ITrie.cs} (51%) create mode 100644 src/neo/Trie/MPT/MPTReadOnlyTrie.cs diff --git a/src/neo/Trie/IReadOnlyTrie.cs b/src/neo/Trie/IReadOnlyTrie.cs new file mode 100644 index 0000000000..a8890b5a01 --- /dev/null +++ b/src/neo/Trie/IReadOnlyTrie.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Neo.Trie +{ + public interface IReadOnlyTrie + { + bool TryGet(byte[] path, out byte[] value); + + byte[] GetRoot(); + + Dictionary GetProof(byte[] path); + + bool VerifyProof(byte[] path, Dictionary proof); + } +} diff --git a/src/neo/Trie/Trie.cs b/src/neo/Trie/ITrie.cs similarity index 51% rename from src/neo/Trie/Trie.cs rename to src/neo/Trie/ITrie.cs index 9caf1a07e4..a4cff28aaf 100644 --- a/src/neo/Trie/Trie.cs +++ b/src/neo/Trie/ITrie.cs @@ -3,18 +3,12 @@ namespace Neo.Trie { - public interface ITrie + public interface ITrie : IReadOnlyTrie { - bool TryGet(byte[] path, out byte[] value); - bool Put(byte[] path, byte[] value); bool TryDelete(byte[] path); - byte[] GetRoot(); - - Dictionary GetProof(byte[] Key); - void Commit(); } } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs new file mode 100644 index 0000000000..e0cd157b6d --- /dev/null +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -0,0 +1,145 @@ +using Neo.Persistence; +using System; +using System.Collections.Generic; + +namespace Neo.Trie.MPT +{ + public class MPTReadOnlyTrie : IReadOnlyTrie + { + protected MPTDatabase db; + protected MPTNode root; + + public MPTReadOnlyTrie(ISnapshot store) + { + if (store is null) + throw new System.ArgumentNullException(); + + this.db = new MPTDatabase(store); + + var rbytes = db.GetRoot(); + if (rbytes is null || rbytes.Length == 0) + { + this.root = HashNode.EmptyNode(); + } + else + { + this.root = Resolve(rbytes); + } + } + + public MPTNode Resolve(byte[] hash) + { + return db.Node(hash); + } + + public bool TryGet(byte[] path, out byte[] value) + { + path = path.ToNibbles(); + return TryGet(ref root, path, out value); + } + + private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) + { + switch (node) + { + case ValueNode valueNode: + { + if (path.Length == 0) + { + value = (byte[])valueNode.Value.Clone(); + return true; + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) break; + node = Resolve(hashNode.Hash); + return TryGet(ref node, path, out value); + } + case FullNode fullNode: + { + if (path.Length == 0) + { + return TryGet(ref fullNode.Children[16], path, out value); + } + return TryGet(ref fullNode.Children[path[0]], path.Skip(1), out value); + } + case ShortNode shortNode: + { + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + return TryGet(ref shortNode.Next, path.Skip(prefix.Length), out value); + } + break; + } + } + value = Array.Empty(); + return false; + } + + public byte[] GetRoot() + { + return this.root.GetHash(); + } + + public Dictionary GetProof(byte[] path) + { + var dict = new Dictionary { }; + path = path.ToNibbles(); + GetProof(ref root, path, dict); + return dict; + } + + private void GetProof(ref MPTNode node, byte[] path, Dictionary dict) + { + switch (node) + { + case ValueNode valueNode: + { + if (path.Length == 0) + { + dict.Add(valueNode.GetHash(), valueNode.Encode()); + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) break; + node = Resolve(hashNode.Hash); + GetProof(ref node, path, dict); + break; + } + case FullNode fullNode: + { + dict.Add(fullNode.GetHash(), fullNode.Encode()); + if (path.Length == 0) + { + GetProof(ref fullNode.Children[16], path, dict); + } + else + { + GetProof(ref fullNode.Children[path[0]], path.Skip(1), dict); + } + break; + } + case ShortNode shortNode: + { + var prefix = shortNode.Key.CommonPrefix(path); + if (prefix.Length == shortNode.Key.Length) + { + dict.Add(shortNode.GetHash(), shortNode.Encode()); + GetProof(ref shortNode.Next, path.Skip(prefix.Length), dict); + } + break; + } + } + } + + public bool VerifyProof(byte[] path, Dictionary proof) + { + return true; + } + } +} diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index d682887e89..1dbb9af57b 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -4,80 +4,9 @@ namespace Neo.Trie.MPT { - public class MPTTrie : ITrie + public class MPTTrie : MPTReadOnlyTrie, ITrie { - private MPTDatabase db; - private MPTNode root; - - public MPTTrie(ISnapshot store) - { - if (store is null) - throw new System.ArgumentNullException(); - - this.db = new MPTDatabase(store); - - var rbytes = db.GetRoot(); - if (rbytes is null || rbytes.Length == 0) - { - this.root = HashNode.EmptyNode(); - } - else - { - this.root = Resolve(rbytes); - } - } - - public MPTNode Resolve(byte[] hash) - { - return db.Node(hash); - } - - public bool TryGet(byte[] path, out byte[] value) - { - path = path.ToNibbles(); - return TryGet(ref root, path, out value); - } - - private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) - { - switch (node) - { - case ValueNode valueNode: - { - if (path.Length == 0) - { - value = (byte[])valueNode.Value.Clone(); - return true; - } - break; - } - case HashNode hashNode: - { - if (hashNode.IsEmptyNode) break; - node = Resolve(hashNode.Hash); - return TryGet(ref node, path, out value); - } - case FullNode fullNode: - { - if (path.Length == 0) - { - return TryGet(ref fullNode.Children[16], path, out value); - } - return TryGet(ref fullNode.Children[path[0]], path.Skip(1), out value); - } - case ShortNode shortNode: - { - var prefix = shortNode.Key.CommonPrefix(path); - if (prefix.Length == shortNode.Key.Length) - { - return TryGet(ref shortNode.Next, path.Skip(prefix.Length), out value); - } - break; - } - } - value = Array.Empty(); - return false; - } + public MPTTrie(ISnapshot store) : base(store) { } public bool Put(byte[] path, byte[] value) { @@ -307,64 +236,6 @@ private bool TryDelete(ref MPTNode node, byte[] path) } } - public byte[] GetRoot() - { - return this.root.GetHash(); - } - - public Dictionary GetProof(byte[] path) - { - var dict = new Dictionary { }; - path = path.ToNibbles(); - GetProof(ref root, path, dict); - return dict; - } - - private void GetProof(ref MPTNode node, byte[] path, Dictionary dict) - { - switch (node) - { - case ValueNode valueNode: - { - if (path.Length == 0) - { - dict.Add(valueNode.GetHash(), valueNode.Encode()); - } - break; - } - case HashNode hashNode: - { - if (hashNode.IsEmptyNode) break; - node = Resolve(hashNode.Hash); - GetProof(ref node, path, dict); - break; - } - case FullNode fullNode: - { - dict.Add(fullNode.GetHash(), fullNode.Encode()); - if (path.Length == 0) - { - GetProof(ref fullNode.Children[16], path, dict); - } - else - { - GetProof(ref fullNode.Children[path[0]], path.Skip(1), dict); - } - break; - } - case ShortNode shortNode: - { - var prefix = shortNode.Key.CommonPrefix(path); - if (prefix.Length == shortNode.Key.Length) - { - dict.Add(shortNode.GetHash(), shortNode.Encode()); - GetProof(ref shortNode.Next, path.Skip(prefix.Length), dict); - } - break; - } - } - } - public void Commit() { if (root.Flag.Dirty) From 59d0c6043f31d7d02d5015814f96c6a7063bf59b Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 24 Feb 2020 16:38:22 +0800 Subject: [PATCH 058/171] change name MPTDatabase to MPTDb --- src/neo/Trie/MPT/{MPTDatabase.cs => MPTDb.cs} | 4 ++-- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 4 ++-- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/neo/Trie/MPT/{MPTDatabase.cs => MPTDb.cs} (92%) diff --git a/src/neo/Trie/MPT/MPTDatabase.cs b/src/neo/Trie/MPT/MPTDb.cs similarity index 92% rename from src/neo/Trie/MPT/MPTDatabase.cs rename to src/neo/Trie/MPT/MPTDb.cs index 314e6854f8..2ca4ad9383 100644 --- a/src/neo/Trie/MPT/MPTDatabase.cs +++ b/src/neo/Trie/MPT/MPTDb.cs @@ -3,13 +3,13 @@ namespace Neo.Trie.MPT { - public class MPTDatabase + public class MPTDb { private ISnapshot store; private static readonly byte TABLE = 0x4D; private static readonly byte[] ROOT_KEY = Encoding.ASCII.GetBytes("MPT_ROOT"); - public MPTDatabase(ISnapshot store) + public MPTDb(ISnapshot store) { this.store = store; } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index e0cd157b6d..7b428c0919 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -6,7 +6,7 @@ namespace Neo.Trie.MPT { public class MPTReadOnlyTrie : IReadOnlyTrie { - protected MPTDatabase db; + protected MPTDb db; protected MPTNode root; public MPTReadOnlyTrie(ISnapshot store) @@ -14,7 +14,7 @@ public MPTReadOnlyTrie(ISnapshot store) if (store is null) throw new System.ArgumentNullException(); - this.db = new MPTDatabase(store); + this.db = new MPTDb(store); var rbytes = db.GetRoot(); if (rbytes is null || rbytes.Length == 0) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index d980b51ec3..1d62ac46ee 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -42,7 +42,7 @@ public void TestInit() root = r; var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var db = new MPTDatabase(snapshot); + var db = new MPTDb(snapshot); db.PutRoot(root.GetHash()); db.Put(r); db.Put(b); From 5c1013cbaa36cbf33c68b0f74279d5ad7f07061d Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 25 Feb 2020 19:03:17 +0800 Subject: [PATCH 059/171] 1. remove value node delete, same value has same hash and share data in db. 2. add tojson to view node tree. 3. fix ref assign --- src/neo/Trie/MPT/MPTNode.cs | 7 +++-- src/neo/Trie/MPT/MPTNode/FullNode.cs | 12 +++++++- src/neo/Trie/MPT/MPTNode/HashNode.cs | 8 +++++ src/neo/Trie/MPT/MPTNode/ShortNode.cs | 9 ++++++ src/neo/Trie/MPT/MPTNode/ValueNode.cs | 8 +++++ src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 2 +- src/neo/Trie/MPT/MPTTrie.cs | 21 ++++++------- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 35 +++++++++++++++++++--- 8 files changed, 83 insertions(+), 19 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 4ec05879b9..47541dacc5 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -1,5 +1,4 @@ -using Neo.Cryptography; -using Neo.IO; +using Neo.IO.Json; using System.IO; using System.Text; @@ -33,7 +32,7 @@ public abstract class MPTNode public virtual byte[] GetHash() { - if (!Flag.Dirty) return Flag.Hash; + if (!Flag.Dirty && Flag.Hash.Length > 0) return Flag.Hash; Flag.Hash = GenHash(); Flag.Dirty = false; return (byte[])Flag.Hash.Clone(); @@ -99,5 +98,7 @@ public static MPTNode Decode(byte[] data) } public abstract void DecodeSpecific(BinaryReader reader); + + public abstract JObject ToJson(); } } diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 3c675a1477..710ebe9809 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -1,5 +1,5 @@ using Neo.Cryptography; -using Neo.IO; +using Neo.IO.Json; using System.IO; namespace Neo.Trie.MPT @@ -47,5 +47,15 @@ public override void DecodeSpecific(BinaryReader reader) Children[i] = hashNode; } } + + public override JObject ToJson() + { + var jarr = new JArray(); + for (int i = 0; i < CHILD_COUNT; i++) + { + jarr.Add(Children[i].ToJson()); + } + return jarr; + } } } diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index cca61e2882..3d90ac0b28 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -1,4 +1,5 @@ using Neo.IO; +using Neo.IO.Json; using System; using System.IO; @@ -41,5 +42,12 @@ public override void DecodeSpecific(BinaryReader reader) { Hash = reader.ReadVarBytes(); } + + public override JObject ToJson() + { + var json = new JObject(); + json["hash"] = Hash.ToHexString(); + return json; + } } } diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index b07ca2d582..6b66b9de09 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -1,5 +1,6 @@ using Neo.Cryptography; using Neo.IO; +using Neo.IO.Json; using System.IO; namespace Neo.Trie.MPT @@ -33,5 +34,13 @@ public override void DecodeSpecific(BinaryReader reader) hashNode.DecodeSpecific(reader); Next = hashNode; } + + public override JObject ToJson() + { + var json = new JObject(); + json["key"] = Key.ToHexString(); + json["next"] = Next.ToJson(); + return json; + } } } diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index 2dc963ca37..d3c9d5174f 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -1,5 +1,6 @@ using Neo.Cryptography; using Neo.IO; +using Neo.IO.Json; using System.IO; namespace Neo.Trie.MPT @@ -33,5 +34,12 @@ public override void DecodeSpecific(BinaryReader reader) { Value = reader.ReadVarBytes(); } + + public override JObject ToJson() + { + var json = new JObject(); + json["value"] = Value.ToHexString(); + return json; + } } } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index 7b428c0919..ef48dba8da 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -81,7 +81,7 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) public byte[] GetRoot() { - return this.root.GetHash(); + return root.GetHash(); } public Dictionary GetProof(byte[] path) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 1dbb9af57b..b894c6e955 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,6 +1,6 @@ +using Neo.IO.Json; using Neo.Persistence; using System; -using System.Collections.Generic; namespace Neo.Trie.MPT { @@ -27,9 +27,8 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { if (path.Length == 0 && val is ValueNode v) { - db.Delete(node.GetHash()); - valueNode = v; - db.Put(valueNode); + node = v; + db.Put(node); return true; } return false; @@ -78,7 +77,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) Next = son, }; db.Put(extensionNode); - shortNode = extensionNode; + node = extensionNode; } else { @@ -117,6 +116,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) Next = val, }; node = newNode; + db.Put(val); db.Put(node); return true; } @@ -142,7 +142,6 @@ private bool TryDelete(ref MPTNode node, byte[] path) { if (path.Length == 0) { - db.Delete(valueNode.GetHash()); node = HashNode.EmptyNode(); return true; } @@ -238,10 +237,12 @@ private bool TryDelete(ref MPTNode node, byte[] path) public void Commit() { - if (root.Flag.Dirty) - { - db.PutRoot(GetRoot()); - } + db.PutRoot(GetRoot()); + } + + public JObject ToJson() + { + return root.ToJson(); } } } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 1d62ac46ee..5494eeb0a3 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -97,10 +97,13 @@ public void TestTryPut() var mpt1 = new MPTTrie(mptdb); Assert.AreEqual("743d2d1f400ae407d14ec19d68c5b6d8791633277a8917d75ab97be4ffed7172", mpt1.GetRoot().ToHexString()); var mpt = new MPTTrie(store.GetSnapshot()); - mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); - mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); - mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); - Assert.AreEqual("aae7f2cd9bcd3b3dadca286ccbecf03d7269fb4cc547fa40ba799abb89c3731f", mpt.GetRoot().ToHexString()); + var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); + Assert.IsTrue(result); + result = mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); + Assert.IsTrue(result); + result = mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); + Assert.IsTrue(result); + Assert.AreEqual("743d2d1f400ae407d14ec19d68c5b6d8791633277a8917d75ab97be4ffed7172", mpt.GetRoot().ToHexString()); } [TestMethod] @@ -144,6 +147,30 @@ public void TestTryDelete() Assert.AreEqual("aae7f2cd9bcd3b3dadca286ccbecf03d7269fb4cc547fa40ba799abb89c3731f", mpt.GetRoot().ToHexString()); } + [TestMethod] + public void TestDeleteSameValue() + { + var store = new MemoryStore(); + var snapshot = store.GetSnapshot(); + var mpt = new MPTTrie(snapshot); + var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); + Assert.IsTrue(result); + result = mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes()); + Assert.IsTrue(result); + result = mpt.TryGet("ac01".HexToBytes(), out byte[] value); + Assert.IsTrue(result); + result = mpt.TryGet("ac02".HexToBytes(), out value); + Assert.IsTrue(result); + result = mpt.TryDelete("ac01".HexToBytes()); + result = mpt.TryGet("ac02".HexToBytes(), out value); + Assert.IsTrue(result); + mpt.Commit(); + snapshot.Commit(); + var mpt0 = new MPTTrie(store.GetSnapshot()); + result = mpt0.TryGet("ac02".HexToBytes(), out value); + Assert.IsTrue(result); + } + [TestMethod] public void TestGetProof() { From b31e25555d2f529af2f58f6ae8fcdf4491a58ae4 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 27 Feb 2020 16:10:12 +0800 Subject: [PATCH 060/171] use list instead dictionary --- src/neo/Trie/IReadOnlyTrie.cs | 2 +- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 12 ++++++------ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/neo/Trie/IReadOnlyTrie.cs b/src/neo/Trie/IReadOnlyTrie.cs index a8890b5a01..5aba2ea25b 100644 --- a/src/neo/Trie/IReadOnlyTrie.cs +++ b/src/neo/Trie/IReadOnlyTrie.cs @@ -8,7 +8,7 @@ public interface IReadOnlyTrie byte[] GetRoot(); - Dictionary GetProof(byte[] path); + List GetProof(byte[] path); bool VerifyProof(byte[] path, Dictionary proof); } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index ef48dba8da..b8f854db99 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -84,15 +84,15 @@ public byte[] GetRoot() return root.GetHash(); } - public Dictionary GetProof(byte[] path) + public List GetProof(byte[] path) { - var dict = new Dictionary { }; + var dict = new List { }; path = path.ToNibbles(); GetProof(ref root, path, dict); return dict; } - private void GetProof(ref MPTNode node, byte[] path, Dictionary dict) + private void GetProof(ref MPTNode node, byte[] path, List dict) { switch (node) { @@ -100,7 +100,7 @@ private void GetProof(ref MPTNode node, byte[] path, Dictionary { if (path.Length == 0) { - dict.Add(valueNode.GetHash(), valueNode.Encode()); + dict.Add(valueNode.Encode()); } break; } @@ -113,7 +113,7 @@ private void GetProof(ref MPTNode node, byte[] path, Dictionary } case FullNode fullNode: { - dict.Add(fullNode.GetHash(), fullNode.Encode()); + dict.Add(fullNode.Encode()); if (path.Length == 0) { GetProof(ref fullNode.Children[16], path, dict); @@ -129,7 +129,7 @@ private void GetProof(ref MPTNode node, byte[] path, Dictionary var prefix = shortNode.Key.CommonPrefix(path); if (prefix.Length == shortNode.Key.Length) { - dict.Add(shortNode.GetHash(), shortNode.Encode()); + dict.Add(shortNode.Encode()); GetProof(ref shortNode.Next, path.Skip(prefix.Length), dict); } break; diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 5494eeb0a3..77119b4b0f 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -202,9 +202,18 @@ public void TestGetProof() var mpt = new MPTTrie(mptdb); Assert.AreEqual(r.GetHash().ToHexString(), mpt.GetRoot().ToHexString()); - var dict = mpt.GetProof("ac01".HexToBytes()); - Assert.AreEqual(4, dict.Count); - Assert.IsTrue(dict.TryGetValue(mpt.GetRoot(), out byte[] value)); + var proof = mpt.GetProof("ac01".HexToBytes()); + Assert.AreEqual(4, proof.Count); + bool exist = false; + foreach (byte[] item in proof) + { + if (item.Equal(b.Encode())) + { + exist = true; + break; + } + } + Assert.IsTrue(exist); } } } From 9ec487a0bc169884cdf27c2888cf532e1b4d8705 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 9 Mar 2020 18:27:53 +0800 Subject: [PATCH 061/171] change dict to list --- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index b8f854db99..c4a3d5792b 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -86,13 +86,13 @@ public byte[] GetRoot() public List GetProof(byte[] path) { - var dict = new List { }; + var list = new List { }; path = path.ToNibbles(); - GetProof(ref root, path, dict); - return dict; + GetProof(ref root, path, list); + return list; } - private void GetProof(ref MPTNode node, byte[] path, List dict) + private void GetProof(ref MPTNode node, byte[] path, List list) { switch (node) { @@ -100,7 +100,7 @@ private void GetProof(ref MPTNode node, byte[] path, List dict) { if (path.Length == 0) { - dict.Add(valueNode.Encode()); + list.Add(valueNode.Encode()); } break; } @@ -108,19 +108,19 @@ private void GetProof(ref MPTNode node, byte[] path, List dict) { if (hashNode.IsEmptyNode) break; node = Resolve(hashNode.Hash); - GetProof(ref node, path, dict); + GetProof(ref node, path, list); break; } case FullNode fullNode: { - dict.Add(fullNode.Encode()); + list.Add(fullNode.Encode()); if (path.Length == 0) { - GetProof(ref fullNode.Children[16], path, dict); + GetProof(ref fullNode.Children[16], path, list); } else { - GetProof(ref fullNode.Children[path[0]], path.Skip(1), dict); + GetProof(ref fullNode.Children[path[0]], path.Skip(1), list); } break; } @@ -129,8 +129,8 @@ private void GetProof(ref MPTNode node, byte[] path, List dict) var prefix = shortNode.Key.CommonPrefix(path); if (prefix.Length == shortNode.Key.Length) { - dict.Add(shortNode.Encode()); - GetProof(ref shortNode.Next, path.Skip(prefix.Length), dict); + list.Add(shortNode.Encode()); + GetProof(ref shortNode.Next, path.Skip(prefix.Length), list); } break; } From 4a81a91970e3609df837fa6672a0f418094d641b Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 9 Mar 2020 18:33:21 +0800 Subject: [PATCH 062/171] use hashset --- src/neo/Trie/IReadOnlyTrie.cs | 4 ++-- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/neo/Trie/IReadOnlyTrie.cs b/src/neo/Trie/IReadOnlyTrie.cs index 5aba2ea25b..f2c66cc4a6 100644 --- a/src/neo/Trie/IReadOnlyTrie.cs +++ b/src/neo/Trie/IReadOnlyTrie.cs @@ -8,8 +8,8 @@ public interface IReadOnlyTrie byte[] GetRoot(); - List GetProof(byte[] path); + HashSet GetProof(byte[] path); - bool VerifyProof(byte[] path, Dictionary proof); + bool VerifyProof(byte[] path, HashSet proof); } } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index c4a3d5792b..ddb56349a0 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -84,15 +84,15 @@ public byte[] GetRoot() return root.GetHash(); } - public List GetProof(byte[] path) + public HashSet GetProof(byte[] path) { - var list = new List { }; + var set = new HashSet { }; path = path.ToNibbles(); - GetProof(ref root, path, list); - return list; + GetProof(ref root, path, set); + return set; } - private void GetProof(ref MPTNode node, byte[] path, List list) + private void GetProof(ref MPTNode node, byte[] path, HashSet set) { switch (node) { @@ -100,7 +100,7 @@ private void GetProof(ref MPTNode node, byte[] path, List list) { if (path.Length == 0) { - list.Add(valueNode.Encode()); + set.Add(valueNode.Encode()); } break; } @@ -108,19 +108,19 @@ private void GetProof(ref MPTNode node, byte[] path, List list) { if (hashNode.IsEmptyNode) break; node = Resolve(hashNode.Hash); - GetProof(ref node, path, list); + GetProof(ref node, path, set); break; } case FullNode fullNode: { - list.Add(fullNode.Encode()); + set.Add(fullNode.Encode()); if (path.Length == 0) { - GetProof(ref fullNode.Children[16], path, list); + GetProof(ref fullNode.Children[16], path, set); } else { - GetProof(ref fullNode.Children[path[0]], path.Skip(1), list); + GetProof(ref fullNode.Children[path[0]], path.Skip(1), set); } break; } @@ -129,15 +129,15 @@ private void GetProof(ref MPTNode node, byte[] path, List list) var prefix = shortNode.Key.CommonPrefix(path); if (prefix.Length == shortNode.Key.Length) { - list.Add(shortNode.Encode()); - GetProof(ref shortNode.Next, path.Skip(prefix.Length), list); + set.Add(shortNode.Encode()); + GetProof(ref shortNode.Next, path.Skip(prefix.Length), set); } break; } } } - public bool VerifyProof(byte[] path, Dictionary proof) + public bool VerifyProof(byte[] path, HashSet proof) { return true; } From 1e36f3b9457fea91e3349f404206fa06baab0971 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 18 Mar 2020 16:50:30 +0800 Subject: [PATCH 063/171] add read only trie, and fix branch node only remain value child --- src/neo/Trie/IKVReadOnlyStore.cs | 7 ++ src/neo/Trie/IKVStore.cs | 7 ++ src/neo/Trie/IReadOnlyTrie.cs | 4 +- src/neo/Trie/ITrie.cs | 2 - src/neo/Trie/MPT/MPTDb.cs | 36 ++---- src/neo/Trie/MPT/MPTNode.cs | 8 +- src/neo/Trie/MPT/MPTNode/FullNode.cs | 10 -- src/neo/Trie/MPT/MPTNode/ShortNode.cs | 5 - src/neo/Trie/MPT/MPTNode/ValueNode.cs | 5 - src/neo/Trie/MPT/MPTProofDb.cs | 24 ++++ src/neo/Trie/MPT/MPTReadOnlyDb.cs | 21 ++++ src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 53 ++++----- src/neo/Trie/MPT/MPTTrie.cs | 42 ++++--- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 122 ++++++++++++++------- 14 files changed, 204 insertions(+), 142 deletions(-) create mode 100644 src/neo/Trie/IKVReadOnlyStore.cs create mode 100644 src/neo/Trie/IKVStore.cs create mode 100644 src/neo/Trie/MPT/MPTProofDb.cs create mode 100644 src/neo/Trie/MPT/MPTReadOnlyDb.cs diff --git a/src/neo/Trie/IKVReadOnlyStore.cs b/src/neo/Trie/IKVReadOnlyStore.cs new file mode 100644 index 0000000000..38e372f1e6 --- /dev/null +++ b/src/neo/Trie/IKVReadOnlyStore.cs @@ -0,0 +1,7 @@ +namespace Neo.Trie +{ + public interface IKVReadOnlyStore + { + byte[] Get(byte[] key); + } +} diff --git a/src/neo/Trie/IKVStore.cs b/src/neo/Trie/IKVStore.cs new file mode 100644 index 0000000000..d689457d78 --- /dev/null +++ b/src/neo/Trie/IKVStore.cs @@ -0,0 +1,7 @@ +namespace Neo.Trie +{ + public interface IKVStore : IKVReadOnlyStore + { + void Put(byte[] key, byte[] value); + } +} diff --git a/src/neo/Trie/IReadOnlyTrie.cs b/src/neo/Trie/IReadOnlyTrie.cs index f2c66cc4a6..6a6f4aa0b4 100644 --- a/src/neo/Trie/IReadOnlyTrie.cs +++ b/src/neo/Trie/IReadOnlyTrie.cs @@ -8,8 +8,6 @@ public interface IReadOnlyTrie byte[] GetRoot(); - HashSet GetProof(byte[] path); - - bool VerifyProof(byte[] path, HashSet proof); + bool GetProof(byte[] path, out HashSet set); } } diff --git a/src/neo/Trie/ITrie.cs b/src/neo/Trie/ITrie.cs index a4cff28aaf..56e4ef3550 100644 --- a/src/neo/Trie/ITrie.cs +++ b/src/neo/Trie/ITrie.cs @@ -8,7 +8,5 @@ public interface ITrie : IReadOnlyTrie bool Put(byte[] path, byte[] value); bool TryDelete(byte[] path); - - void Commit(); } } diff --git a/src/neo/Trie/MPT/MPTDb.cs b/src/neo/Trie/MPT/MPTDb.cs index 2ca4ad9383..5cb176f8f4 100644 --- a/src/neo/Trie/MPT/MPTDb.cs +++ b/src/neo/Trie/MPT/MPTDb.cs @@ -3,42 +3,22 @@ namespace Neo.Trie.MPT { - public class MPTDb + public class MPTDb : MPTReadOnlyDb { - private ISnapshot store; - private static readonly byte TABLE = 0x4D; - private static readonly byte[] ROOT_KEY = Encoding.ASCII.GetBytes("MPT_ROOT"); + private IKVStore store; - public MPTDb(ISnapshot store) + public MPTDb(IKVStore store) : base(store) { this.store = store; } - public MPTNode Node(byte[] hash) - { - var data = store.TryGet(TABLE, hash); - var n = MPTNode.Decode(data); - return n; - } - - public void Delete(byte[] hash) - { - store.Delete(TABLE, hash); - } - public void Put(MPTNode node) { - store.Put(TABLE, node.GetHash(), node.Encode()); - } - - public void PutRoot(byte[] root) - { - store.Put(TABLE, ROOT_KEY, root); - } - - public byte[] GetRoot() - { - return store.TryGet(TABLE, ROOT_KEY); + if (node is HashNode hn) + { + throw new System.InvalidOperationException("Means nothing to store HashNode"); + } + store.Put(node.GetHash(), node.Encode()); } } } diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 47541dacc5..78795a5886 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -1,3 +1,4 @@ +using Neo.Cryptography; using Neo.IO.Json; using System.IO; using System.Text; @@ -28,7 +29,10 @@ public abstract class MPTNode public NodeFlag Flag; protected NodeType nType; - protected abstract byte[] GenHash(); + protected virtual byte[] GenHash() + { + return Crypto.Hash256(this.Encode()); + } public virtual byte[] GetHash() { @@ -65,7 +69,7 @@ public byte[] Encode() public static MPTNode Decode(byte[] data) { if (data is null || data.Length == 0) - throw new System.ArgumentException(); + return null; using (MemoryStream ms = new MemoryStream(data, false)) using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/FullNode.cs index 710ebe9809..6d48d0f833 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/FullNode.cs @@ -19,16 +19,6 @@ public FullNode() } } - protected override byte[] GenHash() - { - var bytes = new byte[0]; - for (int i = 0; i < Children.Length; i++) - { - bytes = bytes.Concat(Children[i].GetHash()); - } - return Crypto.Hash256(bytes); - } - public override void EncodeSpecific(BinaryWriter writer) { for (int i = 0; i < Children.Length; i++) diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ShortNode.cs index 6b66b9de09..3752a73735 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ShortNode.cs @@ -10,11 +10,6 @@ public class ShortNode : MPTNode public byte[] Key; public MPTNode Next; - protected override byte[] GenHash() - { - return Crypto.Hash256(Key.Concat(Next.GetHash())); - } - public ShortNode() { nType = NodeType.ShortNode; diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index d3c9d5174f..1153167d44 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -9,11 +9,6 @@ public class ValueNode : MPTNode { public byte[] Value; - protected override byte[] GenHash() - { - return Value.Length < 32 ? (byte[])Value.Clone() : Crypto.Hash256(Value); - } - public ValueNode() { nType = NodeType.ValueNode; diff --git a/src/neo/Trie/MPT/MPTProofDb.cs b/src/neo/Trie/MPT/MPTProofDb.cs new file mode 100644 index 0000000000..ab9ef8bf11 --- /dev/null +++ b/src/neo/Trie/MPT/MPTProofDb.cs @@ -0,0 +1,24 @@ +using Neo.Persistence; +using Neo.Cryptography; +using System.Collections.Generic; + +namespace Neo.Trie.MPT +{ + public class MPTProofStore : IKVReadOnlyStore + { + private Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); + public MPTProofStore(HashSet proof) + { + foreach (byte[] data in proof) + { + store.Add(Crypto.Hash256(data), data); + } + } + + public byte[] Get(byte[] hash) + { + var result = store.TryGetValue(hash, out byte[] value); + return result ? value : null; + } + } +} diff --git a/src/neo/Trie/MPT/MPTReadOnlyDb.cs b/src/neo/Trie/MPT/MPTReadOnlyDb.cs new file mode 100644 index 0000000000..5e7f31a708 --- /dev/null +++ b/src/neo/Trie/MPT/MPTReadOnlyDb.cs @@ -0,0 +1,21 @@ +using Neo.Persistence; +using System.Text; + +namespace Neo.Trie.MPT +{ + public class MPTReadOnlyDb + { + private IKVReadOnlyStore store; + + public MPTReadOnlyDb(IKVReadOnlyStore store) + { + this.store = store; + } + + public MPTNode Node(byte[] hash) + { + var data = store.Get(hash); + return MPTNode.Decode(data); + } + } +} diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index ddb56349a0..f316a3d67b 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -6,30 +6,29 @@ namespace Neo.Trie.MPT { public class MPTReadOnlyTrie : IReadOnlyTrie { - protected MPTDb db; + private MPTReadOnlyDb rodb; protected MPTNode root; - public MPTReadOnlyTrie(ISnapshot store) + public MPTReadOnlyTrie(byte[] root, IKVReadOnlyStore store) { if (store is null) throw new System.ArgumentNullException(); - this.db = new MPTDb(store); + this.rodb = new MPTReadOnlyDb(store); - var rbytes = db.GetRoot(); - if (rbytes is null || rbytes.Length == 0) + if (root is null || root.Length == 0) { this.root = HashNode.EmptyNode(); } else { - this.root = Resolve(rbytes); + this.root = new HashNode(root); } } - public MPTNode Resolve(byte[] hash) + public MPTNode Resolve(HashNode hn) { - return db.Node(hash); + return rodb.Node(hn.Hash); } public bool TryGet(byte[] path, out byte[] value) @@ -54,7 +53,9 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) case HashNode hashNode: { if (hashNode.IsEmptyNode) break; - node = Resolve(hashNode.Hash); + var new_node = Resolve(hashNode); + if (new_node is null) throw new System.ArgumentNullException("Invalid hash node"); + node = new_node; return TryGet(ref node, path, out value); } case FullNode fullNode: @@ -84,15 +85,14 @@ public byte[] GetRoot() return root.GetHash(); } - public HashSet GetProof(byte[] path) + public bool GetProof(byte[] path, out HashSet set) { - var set = new HashSet { }; + set = new HashSet(ByteArrayEqualityComparer.Default); path = path.ToNibbles(); - GetProof(ref root, path, set); - return set; + return GetProof(ref root, path, set); } - private void GetProof(ref MPTNode node, byte[] path, HashSet set) + private bool GetProof(ref MPTNode node, byte[] path, HashSet set) { switch (node) { @@ -101,28 +101,26 @@ private void GetProof(ref MPTNode node, byte[] path, HashSet set) if (path.Length == 0) { set.Add(valueNode.Encode()); + return true; } break; } case HashNode hashNode: { if (hashNode.IsEmptyNode) break; - node = Resolve(hashNode.Hash); - GetProof(ref node, path, set); - break; + var new_node = Resolve(hashNode); + if (new_node is null) throw new System.ArgumentNullException("Invalid hash node"); + node = new_node; + return GetProof(ref node, path, set); } case FullNode fullNode: { set.Add(fullNode.Encode()); if (path.Length == 0) { - GetProof(ref fullNode.Children[16], path, set); - } - else - { - GetProof(ref fullNode.Children[path[0]], path.Skip(1), set); + return GetProof(ref fullNode.Children[16], path, set); } - break; + return GetProof(ref fullNode.Children[path[0]], path.Skip(1), set); } case ShortNode shortNode: { @@ -130,16 +128,19 @@ private void GetProof(ref MPTNode node, byte[] path, HashSet set) if (prefix.Length == shortNode.Key.Length) { set.Add(shortNode.Encode()); - GetProof(ref shortNode.Next, path.Skip(prefix.Length), set); + return GetProof(ref shortNode.Next, path.Skip(prefix.Length), set); } break; } } + return false; } - public bool VerifyProof(byte[] path, HashSet proof) + public static bool VerifyProof(byte[] root, byte[] path, HashSet proof, out byte[] value) { - return true; + var store = new MPTProofStore(proof); + var trie = new MPTReadOnlyTrie(root, store); + return trie.TryGet(path, out value); } } } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index b894c6e955..da1518a466 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,12 +1,15 @@ using Neo.IO.Json; -using Neo.Persistence; using System; namespace Neo.Trie.MPT { public class MPTTrie : MPTReadOnlyTrie, ITrie { - public MPTTrie(ISnapshot store) : base(store) { } + private MPTDb db; + public MPTTrie(byte[] root, IKVStore store) : base(root, store) + { + this.db = new MPTDb(store); + } public bool Put(byte[] path, byte[] value) { @@ -36,13 +39,11 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) case ShortNode shortNode: { var prefix = shortNode.Key.CommonPrefix(path); - var oldHash = shortNode.GetHash(); if (prefix.Length == shortNode.Key.Length) { var result = Put(ref shortNode.Next, path.Skip(prefix.Length), val); if (result) { - db.Delete(oldHash); shortNode.ResetFlag(); db.Put(shortNode); } @@ -83,13 +84,11 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { node = son; } - db.Delete(oldHash); return true; } case FullNode fullNode: { var result = false; - var oldHash = fullNode.GetHash(); if (path.Length == 0) { result = Put(ref fullNode.Children[FullNode.CHILD_COUNT - 1], path, val); @@ -100,7 +99,6 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } if (result) { - db.Delete(oldHash); fullNode.ResetFlag(); db.Put(fullNode); } @@ -116,11 +114,12 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) Next = val, }; node = newNode; - db.Put(val); + if (!(val is HashNode)) db.Put(val); db.Put(node); return true; } - node = Resolve(hashNode.Hash); + var new_node = Resolve(hashNode); + if (new_node is null) throw new System.ArgumentNullException("Invalid hash node"); return Put(ref node, path, val); } default: @@ -150,12 +149,10 @@ private bool TryDelete(ref MPTNode node, byte[] path) case ShortNode shortNode: { var prefix = shortNode.Key.CommonPrefix(path); - var oldHash = shortNode.GetHash(); if (prefix.Length == shortNode.Key.Length) { var result = TryDelete(ref shortNode.Next, path.Skip(prefix.Length)); if (!result) return false; - db.Delete(oldHash); if (shortNode.Next is HashNode hashNode && hashNode.IsEmptyNode) { node = shortNode.Next; @@ -165,7 +162,6 @@ private bool TryDelete(ref MPTNode node, byte[] path) { shortNode.Key = shortNode.Key.Concat(sn.Key); shortNode.Next = sn.Next; - db.Delete(sn.GetHash()); } shortNode.ResetFlag(); db.Put(shortNode); @@ -176,7 +172,6 @@ private bool TryDelete(ref MPTNode node, byte[] path) case FullNode fullNode: { var result = false; - var oldHash = fullNode.GetHash(); if (path.Length == 0) { result = TryDelete(ref fullNode.Children[FullNode.CHILD_COUNT - 1], path); @@ -186,7 +181,6 @@ private bool TryDelete(ref MPTNode node, byte[] path) result = TryDelete(ref fullNode.Children[path[0]], path.Skip(1)); } if (!result) return false; - db.Delete(oldHash); var childrenIndexes = Array.Empty(); for (int i = 0; i < FullNode.CHILD_COUNT; i++) { @@ -201,11 +195,18 @@ private bool TryDelete(ref MPTNode node, byte[] path) } var lastChildIndex = childrenIndexes[0]; var lastChild = fullNode.Children[lastChildIndex]; + if (lastChildIndex == FullNode.CHILD_COUNT - 1) + { + node = lastChild; + return true; + } if (lastChild is HashNode hashNode) - lastChild = Resolve(hashNode.Hash); + { + lastChild = Resolve(hashNode); + if (lastChild is null) throw new System.ArgumentNullException("Invalid hash node"); + } if (lastChild is ShortNode shortNode) { - db.Delete(shortNode.GetHash()); shortNode.Key = childrenIndexes.Concat(shortNode.Key); shortNode.ResetFlag(); db.Put(shortNode); @@ -227,7 +228,9 @@ private bool TryDelete(ref MPTNode node, byte[] path) { return true; } - node = Resolve(hashNode.Hash); + var new_node = Resolve(hashNode); + if (new_node is null) throw new System.ArgumentNullException("Invalid hash node"); + node = new_node; return TryDelete(ref node, path); } default: @@ -235,11 +238,6 @@ private bool TryDelete(ref MPTNode node, byte[] path) } } - public void Commit() - { - db.PutRoot(GetRoot()); - } - public JObject ToJson() { return root.ToJson(); diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 77119b4b0f..1afb9ee89f 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -1,15 +1,42 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Persistence; +using Neo.Trie; using Neo.Trie.MPT; +using System; +using System.Collections.Generic; using System.Text; namespace Neo.UnitTests.Trie.MPT { + class MemoryStore : IKVStore + { + private Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); + + public void Put(byte[] key, byte[] value) + { + store[key] = value; + } + + public void Delete(byte[] key) + { + store.Remove(key); + } + + public byte[] Get(byte[] key) + { + var result = store.TryGetValue(key, out byte[] value); + if (result) return value; + return Array.Empty(); + } + } + [TestClass] public class UT_MPTTrie { private MPTNode root; - private ISnapshot mptdb; + private IKVStore mptdb; + + private byte[] rootHash; [TestInitialize] public void TestInit() @@ -25,13 +52,13 @@ public void TestInit() v1.Value = "abcd".HexToBytes(); var v2 = new ValueNode(); v2.Value = "2222".HexToBytes(); + var v3 = new ValueNode(); + v3.Value = Encoding.ASCII.GetBytes("hello"); var h1 = new HashNode(); - h1.Hash = Encoding.ASCII.GetBytes("hello"); + h1.Hash = v3.GetHash(); var l3 = new ShortNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); - var v3 = new ValueNode(); - v3.Value = Encoding.ASCII.GetBytes("hello"); r.Next = b; b.Children[0] = l1; @@ -41,9 +68,8 @@ public void TestInit() b.Children[10] = l3; root = r; var store = new MemoryStore(); - var snapshot = store.GetSnapshot(); - var db = new MPTDb(snapshot); - db.PutRoot(root.GetHash()); + var db = new MPTDb(store); + this.rootHash = root.GetHash(); db.Put(r); db.Put(b); db.Put(l1); @@ -52,14 +78,13 @@ public void TestInit() db.Put(v1); db.Put(v2); db.Put(v3); - snapshot.Commit(); - this.mptdb = store.GetSnapshot(); + this.mptdb = store; } [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(mptdb); + var mpt = new MPTTrie(rootHash, mptdb); var result = mpt.TryGet("ac01".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.AreEqual("abcd", value.ToHexString()); @@ -84,7 +109,7 @@ public void TestTryGet() [TestMethod] public void TestTryGetResolve() { - var mpt = new MPTTrie(mptdb); + var mpt = new MPTTrie(rootHash, mptdb); var result = mpt.TryGet("acae".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.IsTrue(Encoding.ASCII.GetBytes("hello").Equal(value)); @@ -94,16 +119,14 @@ public void TestTryGetResolve() public void TestTryPut() { var store = new MemoryStore(); - var mpt1 = new MPTTrie(mptdb); - Assert.AreEqual("743d2d1f400ae407d14ec19d68c5b6d8791633277a8917d75ab97be4ffed7172", mpt1.GetRoot().ToHexString()); - var mpt = new MPTTrie(store.GetSnapshot()); + var mpt = new MPTTrie(null, store); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); Assert.IsTrue(result); - Assert.AreEqual("743d2d1f400ae407d14ec19d68c5b6d8791633277a8917d75ab97be4ffed7172", mpt.GetRoot().ToHexString()); + Assert.AreEqual(rootHash.ToHexString(), mpt.GetRoot().ToHexString()); } [TestMethod] @@ -133,10 +156,10 @@ public void TestTryDelete() b.Children[9] = l2; r1.Next = v1; - Assert.AreEqual("aae7f2cd9bcd3b3dadca286ccbecf03d7269fb4cc547fa40ba799abb89c3731f", r1.GetHash().ToHexString()); - Assert.AreEqual("a388b72bf6f8af80eed633fe95d7397bf51dcb23ceb2026979bb0b831893368b", r.GetHash().ToHexString()); + Assert.AreEqual("b3fba0ac4e39628963bc9cd348b230800a3e531e1c09d75e881e46e946aba3de", r1.GetHash().ToHexString()); + Assert.AreEqual("0dd6476b36e7d5251b9d8d8f4bf61b81cc3b270e3367ca2fd93df8e2ffe1e893", r.GetHash().ToHexString()); - var mpt = new MPTTrie(mptdb); + var mpt = new MPTTrie(rootHash, mptdb); var result = true; result = mpt.TryGet("ac99".HexToBytes(), out byte[] value); Assert.IsTrue(result); @@ -144,15 +167,14 @@ public void TestTryDelete() Assert.IsTrue(result); result = mpt.TryDelete("acae".HexToBytes()); Assert.IsTrue(result); - Assert.AreEqual("aae7f2cd9bcd3b3dadca286ccbecf03d7269fb4cc547fa40ba799abb89c3731f", mpt.GetRoot().ToHexString()); + Assert.AreEqual("b3fba0ac4e39628963bc9cd348b230800a3e531e1c09d75e881e46e946aba3de", mpt.GetRoot().ToHexString()); } [TestMethod] public void TestDeleteSameValue() { var store = new MemoryStore(); - var snapshot = store.GetSnapshot(); - var mpt = new MPTTrie(snapshot); + var mpt = new MPTTrie(null, store); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes()); @@ -164,13 +186,30 @@ public void TestDeleteSameValue() result = mpt.TryDelete("ac01".HexToBytes()); result = mpt.TryGet("ac02".HexToBytes(), out value); Assert.IsTrue(result); - mpt.Commit(); - snapshot.Commit(); - var mpt0 = new MPTTrie(store.GetSnapshot()); + + var mpt0 = new MPTTrie(mpt.GetRoot(), store); result = mpt0.TryGet("ac02".HexToBytes(), out value); Assert.IsTrue(result); } + [TestMethod] + public void TestBranchNodeRemainValue() + { + var store = new MemoryStore(); + var mpt = new MPTTrie(null, store); + var result = mpt.Put("ac11".HexToBytes(), "ac11".HexToBytes()); + Assert.IsTrue(result); + result = mpt.Put("ac22".HexToBytes(), "ac22".HexToBytes()); + Assert.IsTrue(result); + result = mpt.Put("ac".HexToBytes(), "ac".HexToBytes()); + Assert.IsTrue(result); + result = mpt.TryDelete("ac11".HexToBytes()); + Assert.IsTrue(result); + result = mpt.TryDelete("ac22".HexToBytes()); + Assert.IsTrue(result); + Assert.AreEqual("{\"key\":\"0a0c\",\"next\":{\"value\":\"ac\"}}", mpt.ToJson().ToString()); + } + [TestMethod] public void TestGetProof() { @@ -185,13 +224,13 @@ public void TestGetProof() v1.Value = "abcd".HexToBytes(); var v2 = new ValueNode(); v2.Value = "2222".HexToBytes(); + var v3 = new ValueNode(); + v3.Value = Encoding.ASCII.GetBytes("hello"); var h1 = new HashNode(); - h1.Hash = Encoding.ASCII.GetBytes("hello"); + h1.Hash = v3.GetHash(); var l3 = new ShortNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); - var v3 = new ValueNode(); - v3.Value = Encoding.ASCII.GetBytes("hello"); r.Next = b; b.Children[0] = l1; @@ -200,20 +239,25 @@ public void TestGetProof() l2.Next = v2; b.Children[10] = l3; - var mpt = new MPTTrie(mptdb); + var mpt = new MPTTrie(rootHash, mptdb); Assert.AreEqual(r.GetHash().ToHexString(), mpt.GetRoot().ToHexString()); - var proof = mpt.GetProof("ac01".HexToBytes()); + var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); + Assert.IsTrue(result); Assert.AreEqual(4, proof.Count); - bool exist = false; - foreach (byte[] item in proof) - { - if (item.Equal(b.Encode())) - { - exist = true; - break; - } - } - Assert.IsTrue(exist); + Assert.IsTrue(proof.Contains(b.Encode())); + Assert.IsTrue(proof.Contains(r.Encode())); + Assert.IsTrue(proof.Contains(l1.Encode())); + Assert.IsTrue(proof.Contains(v1.Encode())); + } + + [TestMethod] + public void TestVerifyProof() + { + var mpt = new MPTTrie(rootHash, mptdb); + var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); + Assert.IsTrue(result); + result = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof, out byte[] value); + Assert.IsTrue(result); } } } From 3ef79fd2a6f541300d56371fe9a3433454fb7d5c Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 19 Mar 2020 10:02:53 +0800 Subject: [PATCH 064/171] change name --- src/neo/Trie/MPT/MPTNode.cs | 12 +-- .../MPTNode/{FullNode.cs => BranchNode.cs} | 6 +- .../{ShortNode.cs => ExtensionNode.cs} | 6 +- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 38 ++++---- src/neo/Trie/MPT/MPTTrie.cs | 90 +++++++++---------- tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs | 2 +- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 31 +++---- 7 files changed, 93 insertions(+), 92 deletions(-) rename src/neo/Trie/MPT/MPTNode/{FullNode.cs => BranchNode.cs} (91%) rename src/neo/Trie/MPT/MPTNode/{ShortNode.cs => ExtensionNode.cs} (88%) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 78795a5886..50c2d996ff 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -7,8 +7,8 @@ namespace Neo.Trie.MPT { public enum NodeType { - FullNode = 0x00, - ShortNode = 0x01, + BranchNode = 0x00, + ExtensionNode = 0x01, HashNode = 0x02, ValueNode = 0x03, } @@ -77,15 +77,15 @@ public static MPTNode Decode(byte[] data) var nodeType = (NodeType)reader.ReadByte(); switch (nodeType) { - case NodeType.FullNode: + case NodeType.BranchNode: { - var n = new FullNode(); + var n = new BranchNode(); n.DecodeSpecific(reader); return n; } - case NodeType.ShortNode: + case NodeType.ExtensionNode: { - var n = new ShortNode(); + var n = new ExtensionNode(); n.DecodeSpecific(reader); return n; } diff --git a/src/neo/Trie/MPT/MPTNode/FullNode.cs b/src/neo/Trie/MPT/MPTNode/BranchNode.cs similarity index 91% rename from src/neo/Trie/MPT/MPTNode/FullNode.cs rename to src/neo/Trie/MPT/MPTNode/BranchNode.cs index 6d48d0f833..28ccca12ac 100644 --- a/src/neo/Trie/MPT/MPTNode/FullNode.cs +++ b/src/neo/Trie/MPT/MPTNode/BranchNode.cs @@ -4,15 +4,15 @@ namespace Neo.Trie.MPT { - public class FullNode : MPTNode + public class BranchNode : MPTNode { public const int CHILD_COUNT = 17; public MPTNode[] Children = new MPTNode[CHILD_COUNT]; - public FullNode() + public BranchNode() { - nType = NodeType.FullNode; + nType = NodeType.BranchNode; for (int i = 0; i < Children.Length; i++) { Children[i] = HashNode.EmptyNode(); diff --git a/src/neo/Trie/MPT/MPTNode/ShortNode.cs b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs similarity index 88% rename from src/neo/Trie/MPT/MPTNode/ShortNode.cs rename to src/neo/Trie/MPT/MPTNode/ExtensionNode.cs index 3752a73735..2ae623128b 100644 --- a/src/neo/Trie/MPT/MPTNode/ShortNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs @@ -5,14 +5,14 @@ namespace Neo.Trie.MPT { - public class ShortNode : MPTNode + public class ExtensionNode : MPTNode { public byte[] Key; public MPTNode Next; - public ShortNode() + public ExtensionNode() { - nType = NodeType.ShortNode; + nType = NodeType.ExtensionNode; } public override void EncodeSpecific(BinaryWriter writer) diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index f316a3d67b..f642b5f2f0 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -53,25 +53,25 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) case HashNode hashNode: { if (hashNode.IsEmptyNode) break; - var new_node = Resolve(hashNode); - if (new_node is null) throw new System.ArgumentNullException("Invalid hash node"); - node = new_node; + var newNode = Resolve(hashNode); + if (newNode is null) throw new System.ArgumentNullException("Invalid hash node"); + node = newNode; return TryGet(ref node, path, out value); } - case FullNode fullNode: + case BranchNode branchNode: { if (path.Length == 0) { - return TryGet(ref fullNode.Children[16], path, out value); + return TryGet(ref branchNode.Children[16], path, out value); } - return TryGet(ref fullNode.Children[path[0]], path.Skip(1), out value); + return TryGet(ref branchNode.Children[path[0]], path.Skip(1), out value); } - case ShortNode shortNode: + case ExtensionNode extensionNode: { - var prefix = shortNode.Key.CommonPrefix(path); - if (prefix.Length == shortNode.Key.Length) + var prefix = extensionNode.Key.CommonPrefix(path); + if (prefix.Length == extensionNode.Key.Length) { - return TryGet(ref shortNode.Next, path.Skip(prefix.Length), out value); + return TryGet(ref extensionNode.Next, path.Skip(prefix.Length), out value); } break; } @@ -113,22 +113,22 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) node = new_node; return GetProof(ref node, path, set); } - case FullNode fullNode: + case BranchNode branchNode: { - set.Add(fullNode.Encode()); + set.Add(branchNode.Encode()); if (path.Length == 0) { - return GetProof(ref fullNode.Children[16], path, set); + return GetProof(ref branchNode.Children[16], path, set); } - return GetProof(ref fullNode.Children[path[0]], path.Skip(1), set); + return GetProof(ref branchNode.Children[path[0]], path.Skip(1), set); } - case ShortNode shortNode: + case ExtensionNode extensionNode: { - var prefix = shortNode.Key.CommonPrefix(path); - if (prefix.Length == shortNode.Key.Length) + var prefix = extensionNode.Key.CommonPrefix(path); + if (prefix.Length == extensionNode.Key.Length) { - set.Add(shortNode.Encode()); - return GetProof(ref shortNode.Next, path.Skip(prefix.Length), set); + set.Add(extensionNode.Encode()); + return GetProof(ref extensionNode.Next, path.Skip(prefix.Length), set); } break; } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index da1518a466..dbe92c5244 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -36,33 +36,33 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } return false; } - case ShortNode shortNode: + case ExtensionNode extensionNode: { - var prefix = shortNode.Key.CommonPrefix(path); - if (prefix.Length == shortNode.Key.Length) + var prefix = extensionNode.Key.CommonPrefix(path); + if (prefix.Length == extensionNode.Key.Length) { - var result = Put(ref shortNode.Next, path.Skip(prefix.Length), val); + var result = Put(ref extensionNode.Next, path.Skip(prefix.Length), val); if (result) { - shortNode.ResetFlag(); - db.Put(shortNode); + extensionNode.ResetFlag(); + db.Put(extensionNode); } return result; } var pathRemain = path.Skip(prefix.Length); - var keyRemain = shortNode.Key.Skip(prefix.Length); - var son = new FullNode(); + var keyRemain = extensionNode.Key.Skip(prefix.Length); + var son = new BranchNode(); MPTNode grandSon1 = HashNode.EmptyNode(); MPTNode grandSon2 = HashNode.EmptyNode(); - Put(ref grandSon1, keyRemain.Skip(1), shortNode.Next); + Put(ref grandSon1, keyRemain.Skip(1), extensionNode.Next); son.Children[keyRemain[0]] = grandSon1; if (pathRemain.Length == 0) { Put(ref grandSon2, pathRemain, val); - son.Children[FullNode.CHILD_COUNT - 1] = grandSon2; + son.Children[BranchNode.CHILD_COUNT - 1] = grandSon2; } else { @@ -72,13 +72,13 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) db.Put(son); if (prefix.Length > 0) { - var extensionNode = new ShortNode() + var exNode = new ExtensionNode() { Key = prefix, Next = son, }; - db.Put(extensionNode); - node = extensionNode; + db.Put(exNode); + node = exNode; } else { @@ -86,21 +86,21 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } return true; } - case FullNode fullNode: + case BranchNode branchNode: { var result = false; if (path.Length == 0) { - result = Put(ref fullNode.Children[FullNode.CHILD_COUNT - 1], path, val); + result = Put(ref branchNode.Children[BranchNode.CHILD_COUNT - 1], path, val); } else { - result = Put(ref fullNode.Children[path[0]], path.Skip(1), val); + result = Put(ref branchNode.Children[path[0]], path.Skip(1), val); } if (result) { - fullNode.ResetFlag(); - db.Put(fullNode); + branchNode.ResetFlag(); + db.Put(branchNode); } return result; } @@ -108,7 +108,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { if (hashNode.IsEmptyNode) { - var newNode = new ShortNode() + var newNode = new ExtensionNode() { Key = path, Next = val, @@ -146,56 +146,56 @@ private bool TryDelete(ref MPTNode node, byte[] path) } return false; } - case ShortNode shortNode: + case ExtensionNode extensionNode: { - var prefix = shortNode.Key.CommonPrefix(path); - if (prefix.Length == shortNode.Key.Length) + var prefix = extensionNode.Key.CommonPrefix(path); + if (prefix.Length == extensionNode.Key.Length) { - var result = TryDelete(ref shortNode.Next, path.Skip(prefix.Length)); + var result = TryDelete(ref extensionNode.Next, path.Skip(prefix.Length)); if (!result) return false; - if (shortNode.Next is HashNode hashNode && hashNode.IsEmptyNode) + if (extensionNode.Next is HashNode hashNode && hashNode.IsEmptyNode) { - node = shortNode.Next; + node = extensionNode.Next; return true; } - if (shortNode.Next is ShortNode sn) + if (extensionNode.Next is ExtensionNode sn) { - shortNode.Key = shortNode.Key.Concat(sn.Key); - shortNode.Next = sn.Next; + extensionNode.Key = extensionNode.Key.Concat(sn.Key); + extensionNode.Next = sn.Next; } - shortNode.ResetFlag(); - db.Put(shortNode); + extensionNode.ResetFlag(); + db.Put(extensionNode); return true; } return false; } - case FullNode fullNode: + case BranchNode branchNode: { var result = false; if (path.Length == 0) { - result = TryDelete(ref fullNode.Children[FullNode.CHILD_COUNT - 1], path); + result = TryDelete(ref branchNode.Children[BranchNode.CHILD_COUNT - 1], path); } else { - result = TryDelete(ref fullNode.Children[path[0]], path.Skip(1)); + result = TryDelete(ref branchNode.Children[path[0]], path.Skip(1)); } if (!result) return false; var childrenIndexes = Array.Empty(); - for (int i = 0; i < FullNode.CHILD_COUNT; i++) + for (int i = 0; i < BranchNode.CHILD_COUNT; i++) { - if (fullNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; + if (branchNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; childrenIndexes = childrenIndexes.Add((byte)i); } if (childrenIndexes.Length > 1) { - fullNode.ResetFlag(); - db.Put(fullNode); + branchNode.ResetFlag(); + db.Put(branchNode); return true; } var lastChildIndex = childrenIndexes[0]; - var lastChild = fullNode.Children[lastChildIndex]; - if (lastChildIndex == FullNode.CHILD_COUNT - 1) + var lastChild = branchNode.Children[lastChildIndex]; + if (lastChildIndex == BranchNode.CHILD_COUNT - 1) { node = lastChild; return true; @@ -205,15 +205,15 @@ private bool TryDelete(ref MPTNode node, byte[] path) lastChild = Resolve(hashNode); if (lastChild is null) throw new System.ArgumentNullException("Invalid hash node"); } - if (lastChild is ShortNode shortNode) + if (lastChild is ExtensionNode exNode) { - shortNode.Key = childrenIndexes.Concat(shortNode.Key); - shortNode.ResetFlag(); - db.Put(shortNode); - node = shortNode; + exNode.Key = childrenIndexes.Concat(exNode.Key); + exNode.ResetFlag(); + db.Put(exNode); + node = exNode; return true; } - var newNode = new ShortNode() + var newNode = new ExtensionNode() { Key = childrenIndexes, Next = lastChild, diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs index d4947ef556..85370b3914 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs @@ -20,7 +20,7 @@ public void TestDecode() [TestMethod] public void TestFlag() { - var n = new ShortNode(); + var n = new ExtensionNode(); Assert.IsTrue(n.Flag.Dirty); } } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 1afb9ee89f..6dfc49304b 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -41,12 +41,12 @@ public class UT_MPTTrie [TestInitialize] public void TestInit() { - var r = new ShortNode(); + var r = new ExtensionNode(); r.Key = "0a0c".HexToBytes(); - var b = new FullNode(); - var l1 = new ShortNode(); + var b = new BranchNode(); + var l1 = new ExtensionNode(); l1.Key = new byte[] { 0x01 }; - var l2 = new ShortNode(); + var l2 = new ExtensionNode(); l2.Key = new byte[] { 0x09 }; var v1 = new ValueNode(); v1.Value = "abcd".HexToBytes(); @@ -56,7 +56,7 @@ public void TestInit() v3.Value = Encoding.ASCII.GetBytes("hello"); var h1 = new HashNode(); h1.Hash = v3.GetHash(); - var l3 = new ShortNode(); + var l3 = new ExtensionNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); @@ -132,23 +132,23 @@ public void TestTryPut() [TestMethod] public void TestTryDelete() { - var r1 = new ShortNode(); + var r1 = new ExtensionNode(); r1.Key = "0a0c0001".HexToBytes(); - var r = new ShortNode(); + var r = new ExtensionNode(); r.Key = "0a0c".HexToBytes(); - var b = new FullNode(); + var b = new BranchNode(); r.Next = b; - var l1 = new ShortNode(); + var l1 = new ExtensionNode(); l1.Key = new byte[] { 0x01 }; var v1 = new ValueNode(); v1.Value = "abcd".HexToBytes(); l1.Next = v1; b.Children[0] = l1; - var l2 = new ShortNode(); + var l2 = new ExtensionNode(); l2.Key = new byte[] { 0x09 }; var v2 = new ValueNode(); v2.Value = "2222".HexToBytes(); @@ -213,12 +213,12 @@ public void TestBranchNodeRemainValue() [TestMethod] public void TestGetProof() { - var r = new ShortNode(); + var r = new ExtensionNode(); r.Key = "0a0c".HexToBytes(); - var b = new FullNode(); - var l1 = new ShortNode(); + var b = new BranchNode(); + var l1 = new ExtensionNode(); l1.Key = new byte[] { 0x01 }; - var l2 = new ShortNode(); + var l2 = new ExtensionNode(); l2.Key = new byte[] { 0x09 }; var v1 = new ValueNode(); v1.Value = "abcd".HexToBytes(); @@ -228,7 +228,7 @@ public void TestGetProof() v3.Value = Encoding.ASCII.GetBytes("hello"); var h1 = new HashNode(); h1.Hash = v3.GetHash(); - var l3 = new ShortNode(); + var l3 = new ExtensionNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); @@ -258,6 +258,7 @@ public void TestVerifyProof() Assert.IsTrue(result); result = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof, out byte[] value); Assert.IsTrue(result); + Assert.AreEqual(value.ToHexString(), "abcd"); } } } From 2c019956bf2d7f7f5c1ddaa350e1f6b0ee04043a Mon Sep 17 00:00:00 2001 From: zhangtao Date: Sat, 21 Mar 2020 13:12:07 +0800 Subject: [PATCH 065/171] format --- src/neo/Trie/MPT/MPTDb.cs | 2 -- src/neo/Trie/MPT/MPTProofDb.cs | 1 + src/neo/Trie/MPT/MPTTrie.cs | 1 + 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDb.cs b/src/neo/Trie/MPT/MPTDb.cs index 5cb176f8f4..0428cb5e8d 100644 --- a/src/neo/Trie/MPT/MPTDb.cs +++ b/src/neo/Trie/MPT/MPTDb.cs @@ -15,9 +15,7 @@ public MPTDb(IKVStore store) : base(store) public void Put(MPTNode node) { if (node is HashNode hn) - { throw new System.InvalidOperationException("Means nothing to store HashNode"); - } store.Put(node.GetHash(), node.Encode()); } } diff --git a/src/neo/Trie/MPT/MPTProofDb.cs b/src/neo/Trie/MPT/MPTProofDb.cs index ab9ef8bf11..5eb29b8de0 100644 --- a/src/neo/Trie/MPT/MPTProofDb.cs +++ b/src/neo/Trie/MPT/MPTProofDb.cs @@ -7,6 +7,7 @@ namespace Neo.Trie.MPT public class MPTProofStore : IKVReadOnlyStore { private Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); + public MPTProofStore(HashSet proof) { foreach (byte[] data in proof) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index dbe92c5244..4169b3cd7a 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -6,6 +6,7 @@ namespace Neo.Trie.MPT public class MPTTrie : MPTReadOnlyTrie, ITrie { private MPTDb db; + public MPTTrie(byte[] root, IKVStore store) : base(root, store) { this.db = new MPTDb(store); From e5f04cb1addd6c3f59d4b4055d16285efbeac9de Mon Sep 17 00:00:00 2001 From: zhangtao Date: Sat, 21 Mar 2020 13:17:47 +0800 Subject: [PATCH 066/171] format --- src/neo/Trie/MPT/MPTProofDb.cs | 2 +- src/neo/Trie/MPT/MPTTrie.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTProofDb.cs b/src/neo/Trie/MPT/MPTProofDb.cs index 5eb29b8de0..284f547cc9 100644 --- a/src/neo/Trie/MPT/MPTProofDb.cs +++ b/src/neo/Trie/MPT/MPTProofDb.cs @@ -7,7 +7,7 @@ namespace Neo.Trie.MPT public class MPTProofStore : IKVReadOnlyStore { private Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); - + public MPTProofStore(HashSet proof) { foreach (byte[] data in proof) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 4169b3cd7a..056032bb38 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -6,7 +6,7 @@ namespace Neo.Trie.MPT public class MPTTrie : MPTReadOnlyTrie, ITrie { private MPTDb db; - + public MPTTrie(byte[] root, IKVStore store) : base(root, store) { this.db = new MPTDb(store); From 4afd976861bd62024e13a25043c3885bbf84aa7e Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 23 Mar 2020 13:53:56 +0800 Subject: [PATCH 067/171] return instead of throw --- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 4 ++-- src/neo/Trie/MPT/MPTTrie.cs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index f642b5f2f0..bb788c43e0 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -54,7 +54,7 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) { if (hashNode.IsEmptyNode) break; var newNode = Resolve(hashNode); - if (newNode is null) throw new System.ArgumentNullException("Invalid hash node"); + if (newNode is null) break; node = newNode; return TryGet(ref node, path, out value); } @@ -109,7 +109,7 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) { if (hashNode.IsEmptyNode) break; var new_node = Resolve(hashNode); - if (new_node is null) throw new System.ArgumentNullException("Invalid hash node"); + if (new_node is null) break; node = new_node; return GetProof(ref node, path, set); } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index dbe92c5244..95e371b689 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -119,11 +119,11 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) return true; } var new_node = Resolve(hashNode); - if (new_node is null) throw new System.ArgumentNullException("Invalid hash node"); + if (new_node is null) return false; return Put(ref node, path, val); } default: - throw new System.InvalidOperationException("Invalid node type."); + return false; } } @@ -203,7 +203,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) if (lastChild is HashNode hashNode) { lastChild = Resolve(hashNode); - if (lastChild is null) throw new System.ArgumentNullException("Invalid hash node"); + if (lastChild is null) return false; } if (lastChild is ExtensionNode exNode) { @@ -229,7 +229,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) return true; } var new_node = Resolve(hashNode); - if (new_node is null) throw new System.ArgumentNullException("Invalid hash node"); + if (new_node is null) return false; node = new_node; return TryDelete(ref node, path); } From 6d11e2eb01178f794ded4bd1f37f75b68d9ea68f Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 25 Mar 2020 15:01:30 +0800 Subject: [PATCH 068/171] mv trie/helper to neo/helper --- src/neo/Helper.cs | 65 ++++++++++++++++++ src/neo/Trie/ITrie.cs | 1 - src/neo/Trie/MPT/Helper.cs | 72 -------------------- src/neo/Trie/MPT/MPTDb.cs | 2 - src/neo/Trie/MPT/MPTReadOnlyDb.cs | 2 - tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 80 ----------------------- tests/neo.UnitTests/UT_Helper.cs | 71 ++++++++++++++++++++ 7 files changed, 136 insertions(+), 157 deletions(-) delete mode 100644 src/neo/Trie/MPT/Helper.cs delete mode 100644 tests/neo.UnitTests/Trie/MPT/UT_Helper.cs diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index be66ce2013..fa77342f36 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -310,5 +310,70 @@ internal static IEnumerable WeightedFilter(this IList so yield return resultSelector(item, weight); } } + + public static byte[] Concat(this byte[] a, byte[] b) + { + var result = new byte[a.Length + b.Length]; + a.CopyTo(result, 0); + b.CopyTo(result, a.Length); + return result; + } + + public static byte[] CommonPrefix(this byte[] a, byte[] b) + { + var prefix = Array.Empty(); + var minLen = a.Length <= b.Length ? a.Length : b.Length; + + if (a.Length != 0 && b.Length != 0) + { + for (int i = 0; i < minLen; i++) + { + if (a[i] != b[i]) break; + prefix = prefix.Add(a[i]); + } + } + return prefix; + } + + public static bool Equal(this byte[] a, byte[] b) + { + if (a.Length != b.Length) return false; + for (int i = 0; i < a.Length; i++) + { + if (a[i] != b[i]) return false; + } + return true; + } + + public static byte[] Skip(this byte[] a, int count) + { + var result = Array.Empty(); + var len = a.Length - count; + if (0 < len) + { + result = new byte[len]; + Array.Copy(a, count, result, 0, len); + } + return result; + } + + public static byte[] Add(this byte[] a, byte b) + { + var result = new byte[a.Length + 1]; + a.CopyTo(result, 0); + result[a.Length] = b; + return result; + } + + public static byte[] ToNibbles(this byte[] path) + { + var result = new byte[path.Length * 2]; + for (int i = 0; i < path.Length; i++) + { + result[i * 2] = (byte)(path[i] >> 4); + result[i * 2 + 1] = (byte)(path[i] & 0x0F); + } + return result; + } } } diff --git a/src/neo/Trie/ITrie.cs b/src/neo/Trie/ITrie.cs index 56e4ef3550..c4152850cd 100644 --- a/src/neo/Trie/ITrie.cs +++ b/src/neo/Trie/ITrie.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; namespace Neo.Trie { diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs deleted file mode 100644 index 81a0fa365c..0000000000 --- a/src/neo/Trie/MPT/Helper.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; - -namespace Neo.Trie.MPT -{ - public static class Helper - { - public static byte[] Concat(this byte[] a, byte[] b) - { - var result = new byte[a.Length + b.Length]; - a.CopyTo(result, 0); - b.CopyTo(result, a.Length); - return result; - } - - public static byte[] CommonPrefix(this byte[] a, byte[] b) - { - var prefix = Array.Empty(); - var minLen = a.Length <= b.Length ? a.Length : b.Length; - - if (a.Length != 0 && b.Length != 0) - { - for (int i = 0; i < minLen; i++) - { - if (a[i] != b[i]) break; - prefix = prefix.Add(a[i]); - } - } - return prefix; - } - - public static bool Equal(this byte[] a, byte[] b) - { - if (a.Length != b.Length) return false; - for (int i = 0; i < a.Length; i++) - { - if (a[i] != b[i]) return false; - } - return true; - } - - public static byte[] Skip(this byte[] a, int count) - { - var result = Array.Empty(); - var len = a.Length - count; - if (0 < len) - { - result = new byte[len]; - Array.Copy(a, count, result, 0, len); - } - return result; - } - - public static byte[] Add(this byte[] a, byte b) - { - var result = new byte[a.Length + 1]; - a.CopyTo(result, 0); - result[a.Length] = b; - return result; - } - - public static byte[] ToNibbles(this byte[] path) - { - var result = new byte[path.Length * 2]; - for (int i = 0; i < path.Length; i++) - { - result[i * 2] = (byte)(path[i] >> 4); - result[i * 2 + 1] = (byte)(path[i] & 0x0F); - } - return result; - } - } -} diff --git a/src/neo/Trie/MPT/MPTDb.cs b/src/neo/Trie/MPT/MPTDb.cs index 0428cb5e8d..41f489be71 100644 --- a/src/neo/Trie/MPT/MPTDb.cs +++ b/src/neo/Trie/MPT/MPTDb.cs @@ -1,5 +1,3 @@ -using Neo.Persistence; -using System.Text; namespace Neo.Trie.MPT { diff --git a/src/neo/Trie/MPT/MPTReadOnlyDb.cs b/src/neo/Trie/MPT/MPTReadOnlyDb.cs index 5e7f31a708..8c27d90c27 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyDb.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyDb.cs @@ -1,5 +1,3 @@ -using Neo.Persistence; -using System.Text; namespace Neo.Trie.MPT { diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs deleted file mode 100644 index ba9f11761c..0000000000 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Trie.MPT; - -namespace Neo.UnitTests.Trie.MPT -{ - [TestClass] - public class UT_Helper - { - [TestMethod] - public void TestConcat() - { - var a = new byte[] { 0x01 }; - var b = new byte[] { 0x02 }; - a = a.Concat(b); - Assert.AreEqual(2, a.Length); - } - - [TestMethod] - public void TestAdd() - { - var a = "ab".HexToBytes(); - byte b = 0x0c; - a = a.Add(b); - Assert.AreEqual("ab0c", a.ToHexString()); - - a = new byte[0]; - a = a.Add(b); - Assert.AreEqual(1, a.Length); - Assert.AreEqual("0c", a.ToHexString()); - } - - [TestMethod] - public void TestSkip() - { - var s = "abcd01".HexToBytes(); - s = s.Skip(2); - Assert.AreEqual("01", s.ToHexString()); - - s = new byte[] { 0x01 }; - s = s.Skip(1); - Assert.AreEqual(0, s.Length); - s = s.Skip(2); - Assert.AreEqual(0, s.Length); - } - - [TestMethod] - public void TestCommonPrefix() - { - var a = "1234abcd".HexToBytes(); - var b = "".HexToBytes(); - var prefix = a.CommonPrefix(b); - Assert.IsTrue(prefix.Length == 0); - - b = "100000".HexToBytes(); - prefix = a.CommonPrefix(b); - Assert.IsTrue(prefix.Length == 0); - - b = "1234".HexToBytes(); - prefix = a.CommonPrefix(b); - Assert.AreEqual("1234", prefix.ToHexString()); - - b = a; - prefix = a.CommonPrefix(b); - Assert.AreEqual("1234abcd", prefix.ToHexString()); - - a = new byte[0]; - b = new byte[0]; - prefix = a.CommonPrefix(b); - Assert.IsTrue(prefix.Length == 0); - } - - [TestMethod] - public void TestToNibbles() - { - var a = "1234abcd".HexToBytes(); - var n = a.ToNibbles(); - Assert.AreEqual("010203040a0b0c0d", n.ToHexString()); - } - } -} diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index daeaabc43b..3376ce6a65 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -255,6 +255,77 @@ private static void CheckArgumentNullException(double start, double end, Func p.Weight, p => p.Weight); action.Should().Throw(); } + + [TestMethod] + public void TestConcat() + { + var a = new byte[] { 0x01 }; + var b = new byte[] { 0x02 }; + a = a.Concat(b); + Assert.AreEqual(2, a.Length); + } + + [TestMethod] + public void TestAdd() + { + var a = "ab".HexToBytes(); + byte b = 0x0c; + a = a.Add(b); + Assert.AreEqual("ab0c", a.ToHexString()); + + a = new byte[0]; + a = a.Add(b); + Assert.AreEqual(1, a.Length); + Assert.AreEqual("0c", a.ToHexString()); + } + + [TestMethod] + public void TestSkip() + { + var s = "abcd01".HexToBytes(); + s = s.Skip(2); + Assert.AreEqual("01", s.ToHexString()); + + s = new byte[] { 0x01 }; + s = s.Skip(1); + Assert.AreEqual(0, s.Length); + s = s.Skip(2); + Assert.AreEqual(0, s.Length); + } + + [TestMethod] + public void TestCommonPrefix() + { + var a = "1234abcd".HexToBytes(); + var b = "".HexToBytes(); + var prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + + b = "100000".HexToBytes(); + prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + + b = "1234".HexToBytes(); + prefix = a.CommonPrefix(b); + Assert.AreEqual("1234", prefix.ToHexString()); + + b = a; + prefix = a.CommonPrefix(b); + Assert.AreEqual("1234abcd", prefix.ToHexString()); + + a = new byte[0]; + b = new byte[0]; + prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + } + + [TestMethod] + public void TestToNibbles() + { + var a = "1234abcd".HexToBytes(); + var n = a.ToNibbles(); + Assert.AreEqual("010203040a0b0c0d", n.ToHexString()); + } } class Foo From 37e5e0299849eafb8d7e3758aece2b1138ed63ba Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 25 Mar 2020 15:41:03 +0100 Subject: [PATCH 069/171] Optimize --- src/neo/Trie/MPT/MPTNode/HashNode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index 3d90ac0b28..3593190ceb 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -14,9 +14,8 @@ public HashNode() nType = NodeType.HashNode; } - public HashNode(byte[] hash) + public HashNode(byte[] hash) : this() { - nType = NodeType.HashNode; Hash = (byte[])hash.Clone(); } From d6a43dd74dab4043fd8e3f592d1490510427d0eb Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 25 Mar 2020 15:43:44 +0100 Subject: [PATCH 070/171] Optimize --- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index 1153167d44..affdded027 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -14,9 +14,8 @@ public ValueNode() nType = NodeType.ValueNode; } - public ValueNode(byte[] val) + public ValueNode(byte[] val) : this() { - nType = NodeType.ValueNode; Value = (byte[])val.Clone(); } From 57a9cf259e5c44cd28ccb4b54b35c5d300ff6cb9 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 25 Mar 2020 16:00:58 +0100 Subject: [PATCH 071/171] Clean file --- src/neo/Trie/MPT/MPTNode/BranchNode.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/BranchNode.cs b/src/neo/Trie/MPT/MPTNode/BranchNode.cs index 28ccca12ac..25a7387996 100644 --- a/src/neo/Trie/MPT/MPTNode/BranchNode.cs +++ b/src/neo/Trie/MPT/MPTNode/BranchNode.cs @@ -1,4 +1,3 @@ -using Neo.Cryptography; using Neo.IO.Json; using System.IO; @@ -9,7 +8,6 @@ public class BranchNode : MPTNode public const int CHILD_COUNT = 17; public MPTNode[] Children = new MPTNode[CHILD_COUNT]; - public BranchNode() { nType = NodeType.BranchNode; From 5905b123c278aa718be714829162fec7ed1bbeeb Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 26 Mar 2020 10:54:54 +0800 Subject: [PATCH 072/171] use Array.Resize --- src/neo/Helper.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index fa77342f36..4272f02c7e 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -359,10 +359,10 @@ public static byte[] Skip(this byte[] a, int count) public static byte[] Add(this byte[] a, byte b) { - var result = new byte[a.Length + 1]; - a.CopyTo(result, 0); - result[a.Length] = b; - return result; + var len = a.Length; + Array.Resize(ref a, len + 1); + a[len] = b; + return a; } public static byte[] ToNibbles(this byte[] path) From 2918ce53f097e1002f4ca0abab5f91668c2ba13d Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 26 Mar 2020 11:20:18 +0800 Subject: [PATCH 073/171] one class one file --- src/neo/Trie/MPT/MPTNode.cs | 35 ++++++---------------- src/neo/Trie/MPT/MPTNodeType.cs | 11 +++++++ src/neo/Trie/MPT/MPTTrie.cs | 10 +++---- tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs | 2 +- 4 files changed, 26 insertions(+), 32 deletions(-) create mode 100644 src/neo/Trie/MPT/MPTNodeType.cs diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 50c2d996ff..19350b3d04 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -5,28 +5,11 @@ namespace Neo.Trie.MPT { - public enum NodeType - { - BranchNode = 0x00, - ExtensionNode = 0x01, - HashNode = 0x02, - ValueNode = 0x03, - } - - public class NodeFlag - { - public byte[] Hash; - public bool Dirty; - - public NodeFlag() - { - Dirty = true; - } - } public abstract class MPTNode { - public NodeFlag Flag; + private byte[] hash; + public bool Dirty { get; private set; } protected NodeType nType; protected virtual byte[] GenHash() @@ -36,20 +19,20 @@ protected virtual byte[] GenHash() public virtual byte[] GetHash() { - if (!Flag.Dirty && Flag.Hash.Length > 0) return Flag.Hash; - Flag.Hash = GenHash(); - Flag.Dirty = false; - return (byte[])Flag.Hash.Clone(); + if (!Dirty && hash.Length > 0) return hash; + hash = GenHash(); + Dirty = false; + return (byte[])hash.Clone(); } - public void ResetFlag() + public void SetDirty() { - Flag = new NodeFlag(); + Dirty = true; } public MPTNode() { - Flag = new NodeFlag(); + Dirty = true; } public byte[] Encode() diff --git a/src/neo/Trie/MPT/MPTNodeType.cs b/src/neo/Trie/MPT/MPTNodeType.cs new file mode 100644 index 0000000000..ac894003cc --- /dev/null +++ b/src/neo/Trie/MPT/MPTNodeType.cs @@ -0,0 +1,11 @@ + +namespace Neo.Trie.MPT +{ + public enum NodeType + { + BranchNode = 0x00, + ExtensionNode = 0x01, + HashNode = 0x02, + ValueNode = 0x03, + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index c6d26bc7f7..9be59e6919 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -45,7 +45,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) var result = Put(ref extensionNode.Next, path.Skip(prefix.Length), val); if (result) { - extensionNode.ResetFlag(); + extensionNode.SetDirty(); db.Put(extensionNode); } return result; @@ -100,7 +100,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } if (result) { - branchNode.ResetFlag(); + branchNode.SetDirty(); db.Put(branchNode); } return result; @@ -164,7 +164,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) extensionNode.Key = extensionNode.Key.Concat(sn.Key); extensionNode.Next = sn.Next; } - extensionNode.ResetFlag(); + extensionNode.SetDirty(); db.Put(extensionNode); return true; } @@ -190,7 +190,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) } if (childrenIndexes.Length > 1) { - branchNode.ResetFlag(); + branchNode.SetDirty(); db.Put(branchNode); return true; } @@ -209,7 +209,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) if (lastChild is ExtensionNode exNode) { exNode.Key = childrenIndexes.Concat(exNode.Key); - exNode.ResetFlag(); + exNode.SetDirty(); db.Put(exNode); node = exNode; return true; diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs index 85370b3914..e38dea4bd4 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs @@ -21,7 +21,7 @@ public void TestDecode() public void TestFlag() { var n = new ExtensionNode(); - Assert.IsTrue(n.Flag.Dirty); + Assert.IsTrue(n.Dirty); } } } From eac80cbfe6cfdef4ff940958dd616b13a7475c40 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 26 Mar 2020 11:27:49 +0800 Subject: [PATCH 074/171] change valuenode to leafnode --- src/neo/Trie/MPT/MPTNode.cs | 4 ++-- src/neo/Trie/MPT/MPTNode/ValueNode.cs | 8 ++++---- src/neo/Trie/MPT/MPTNodeType.cs | 2 +- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 8 ++++---- src/neo/Trie/MPT/MPTTrie.cs | 8 ++++---- tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs | 2 +- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 16 ++++++++-------- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 19350b3d04..16caf45305 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -72,9 +72,9 @@ public static MPTNode Decode(byte[] data) n.DecodeSpecific(reader); return n; } - case NodeType.ValueNode: + case NodeType.LeafNode: { - var n = new ValueNode(); + var n = new LeafNode(); n.DecodeSpecific(reader); return n; } diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/ValueNode.cs index affdded027..b81455549f 100644 --- a/src/neo/Trie/MPT/MPTNode/ValueNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ValueNode.cs @@ -5,16 +5,16 @@ namespace Neo.Trie.MPT { - public class ValueNode : MPTNode + public class LeafNode : MPTNode { public byte[] Value; - public ValueNode() + public LeafNode() { - nType = NodeType.ValueNode; + nType = NodeType.LeafNode; } - public ValueNode(byte[] val) : this() + public LeafNode(byte[] val) : this() { Value = (byte[])val.Clone(); } diff --git a/src/neo/Trie/MPT/MPTNodeType.cs b/src/neo/Trie/MPT/MPTNodeType.cs index ac894003cc..16ef1f88ca 100644 --- a/src/neo/Trie/MPT/MPTNodeType.cs +++ b/src/neo/Trie/MPT/MPTNodeType.cs @@ -6,6 +6,6 @@ public enum NodeType BranchNode = 0x00, ExtensionNode = 0x01, HashNode = 0x02, - ValueNode = 0x03, + LeafNode = 0x03, } } \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index bb788c43e0..09d8d04c84 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -41,11 +41,11 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) { switch (node) { - case ValueNode valueNode: + case LeafNode leafNode: { if (path.Length == 0) { - value = (byte[])valueNode.Value.Clone(); + value = (byte[])leafNode.Value.Clone(); return true; } break; @@ -96,11 +96,11 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) { switch (node) { - case ValueNode valueNode: + case LeafNode leafNode: { if (path.Length == 0) { - set.Add(valueNode.Encode()); + set.Add(leafNode.Encode()); return true; } break; diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 9be59e6919..1cecf67493 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -19,7 +19,7 @@ public bool Put(byte[] path, byte[] value) { return TryDelete(ref root, path); } - var n = new ValueNode(value); + var n = new LeafNode(value); return Put(ref root, path, n); } @@ -27,9 +27,9 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { switch (node) { - case ValueNode valueNode: + case LeafNode leafNode: { - if (path.Length == 0 && val is ValueNode v) + if (path.Length == 0 && val is LeafNode v) { node = v; db.Put(node); @@ -138,7 +138,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) { switch (node) { - case ValueNode valueNode: + case LeafNode leafNode: { if (path.Length == 0) { diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs index e38dea4bd4..122e8be5e2 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs @@ -10,7 +10,7 @@ public class UT_MPTNode [TestMethod] public void TestDecode() { - var n = new ValueNode(); + var n = new LeafNode(); n.Value = Encoding.ASCII.GetBytes("hello"); var code = n.Encode(); var m = MPTNode.Decode(code); diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 6dfc49304b..d15351ed41 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -48,11 +48,11 @@ public void TestInit() l1.Key = new byte[] { 0x01 }; var l2 = new ExtensionNode(); l2.Key = new byte[] { 0x09 }; - var v1 = new ValueNode(); + var v1 = new LeafNode(); v1.Value = "abcd".HexToBytes(); - var v2 = new ValueNode(); + var v2 = new LeafNode(); v2.Value = "2222".HexToBytes(); - var v3 = new ValueNode(); + var v3 = new LeafNode(); v3.Value = Encoding.ASCII.GetBytes("hello"); var h1 = new HashNode(); h1.Hash = v3.GetHash(); @@ -143,14 +143,14 @@ public void TestTryDelete() var l1 = new ExtensionNode(); l1.Key = new byte[] { 0x01 }; - var v1 = new ValueNode(); + var v1 = new LeafNode(); v1.Value = "abcd".HexToBytes(); l1.Next = v1; b.Children[0] = l1; var l2 = new ExtensionNode(); l2.Key = new byte[] { 0x09 }; - var v2 = new ValueNode(); + var v2 = new LeafNode(); v2.Value = "2222".HexToBytes(); l2.Next = v2; b.Children[9] = l2; @@ -220,11 +220,11 @@ public void TestGetProof() l1.Key = new byte[] { 0x01 }; var l2 = new ExtensionNode(); l2.Key = new byte[] { 0x09 }; - var v1 = new ValueNode(); + var v1 = new LeafNode(); v1.Value = "abcd".HexToBytes(); - var v2 = new ValueNode(); + var v2 = new LeafNode(); v2.Value = "2222".HexToBytes(); - var v3 = new ValueNode(); + var v3 = new LeafNode(); v3.Value = Encoding.ASCII.GetBytes("hello"); var h1 = new HashNode(); h1.Hash = v3.GetHash(); From 78b7f2a2bf629dc7897626695e2e2ea5d4a0f725 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 26 Mar 2020 13:57:21 +0800 Subject: [PATCH 075/171] file name --- src/neo/Trie/MPT/MPTNode/{ValueNode.cs => LeafNode.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/neo/Trie/MPT/MPTNode/{ValueNode.cs => LeafNode.cs} (100%) diff --git a/src/neo/Trie/MPT/MPTNode/ValueNode.cs b/src/neo/Trie/MPT/MPTNode/LeafNode.cs similarity index 100% rename from src/neo/Trie/MPT/MPTNode/ValueNode.cs rename to src/neo/Trie/MPT/MPTNode/LeafNode.cs From a0bcb39c1d394ec6b7dbd7d4455ea20d5d9bb63a Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 26 Mar 2020 14:05:34 +0800 Subject: [PATCH 076/171] format --- src/neo/Trie/MPT/MPTNodeType.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTNodeType.cs b/src/neo/Trie/MPT/MPTNodeType.cs index 16ef1f88ca..d9b20c3393 100644 --- a/src/neo/Trie/MPT/MPTNodeType.cs +++ b/src/neo/Trie/MPT/MPTNodeType.cs @@ -8,4 +8,4 @@ public enum NodeType HashNode = 0x02, LeafNode = 0x03, } -} \ No newline at end of file +} From e292b3d2f60d592b8e6a618c7187727853de0fd6 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 26 Mar 2020 15:24:53 +0800 Subject: [PATCH 077/171] use uint256 --- src/neo/Trie/IReadOnlyTrie.cs | 2 +- src/neo/Trie/ITrie.cs | 1 - src/neo/Trie/MPT/MPTDb.cs | 3 +- src/neo/Trie/MPT/MPTNode.cs | 12 +++---- src/neo/Trie/MPT/MPTNode/ExtensionNode.cs | 1 - src/neo/Trie/MPT/MPTNode/HashNode.cs | 33 ++++++++++++------- src/neo/Trie/MPT/MPTNode/LeafNode.cs | 1 - .../MPT/{MPTProofDb.cs => MPTProofStore.cs} | 2 +- src/neo/Trie/MPT/MPTReadOnlyDb.cs | 6 ++-- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 8 ++--- src/neo/Trie/MPT/MPTTrie.cs | 2 +- tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs | 8 +++++ tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 12 +++---- 13 files changed, 55 insertions(+), 36 deletions(-) rename src/neo/Trie/MPT/{MPTProofDb.cs => MPTProofStore.cs} (100%) diff --git a/src/neo/Trie/IReadOnlyTrie.cs b/src/neo/Trie/IReadOnlyTrie.cs index 6a6f4aa0b4..57127955d3 100644 --- a/src/neo/Trie/IReadOnlyTrie.cs +++ b/src/neo/Trie/IReadOnlyTrie.cs @@ -6,7 +6,7 @@ public interface IReadOnlyTrie { bool TryGet(byte[] path, out byte[] value); - byte[] GetRoot(); + UInt256 GetRoot(); bool GetProof(byte[] path, out HashSet set); } diff --git a/src/neo/Trie/ITrie.cs b/src/neo/Trie/ITrie.cs index c4152850cd..07a384edd1 100644 --- a/src/neo/Trie/ITrie.cs +++ b/src/neo/Trie/ITrie.cs @@ -1,5 +1,4 @@ - namespace Neo.Trie { public interface ITrie : IReadOnlyTrie diff --git a/src/neo/Trie/MPT/MPTDb.cs b/src/neo/Trie/MPT/MPTDb.cs index 41f489be71..5a82c32b28 100644 --- a/src/neo/Trie/MPT/MPTDb.cs +++ b/src/neo/Trie/MPT/MPTDb.cs @@ -1,3 +1,4 @@ +using Neo.IO; namespace Neo.Trie.MPT { @@ -14,7 +15,7 @@ public void Put(MPTNode node) { if (node is HashNode hn) throw new System.InvalidOperationException("Means nothing to store HashNode"); - store.Put(node.GetHash(), node.Encode()); + store.Put(node.GetHash().ToArray(), node.Encode()); } } } diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 16caf45305..9cbd3bd343 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -8,21 +8,21 @@ namespace Neo.Trie.MPT public abstract class MPTNode { - private byte[] hash; + private UInt256 hash; public bool Dirty { get; private set; } protected NodeType nType; - protected virtual byte[] GenHash() + protected virtual UInt256 GenHash() { - return Crypto.Hash256(this.Encode()); + return new UInt256(Crypto.Hash256(this.Encode())); } - public virtual byte[] GetHash() + public virtual UInt256 GetHash() { - if (!Dirty && hash.Length > 0) return hash; + if (!Dirty && !(hash is null)) return hash; hash = GenHash(); Dirty = false; - return (byte[])hash.Clone(); + return hash; } public void SetDirty() diff --git a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs index 2ae623128b..4f694cb0c7 100644 --- a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs @@ -1,4 +1,3 @@ -using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; using System.IO; diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index 3593190ceb..c0b13677ad 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -7,45 +7,56 @@ namespace Neo.Trie.MPT { public class HashNode : MPTNode { - public byte[] Hash; + public UInt256 Hash; public HashNode() { nType = NodeType.HashNode; } - public HashNode(byte[] hash) : this() + public HashNode(UInt256 hash) : this() { - Hash = (byte[])hash.Clone(); + Hash = hash; } - protected override byte[] GenHash() + protected override UInt256 GenHash() { - if (IsEmptyNode) return Array.Empty(); - return (byte[])Hash.Clone(); + return Hash; } public static HashNode EmptyNode() { - return new HashNode(Array.Empty()); + return new HashNode(null); } - public bool IsEmptyNode => Hash is null || Hash.Length == 0; + public bool IsEmptyNode => Hash is null; public override void EncodeSpecific(BinaryWriter writer) { - writer.WriteVarBytes(Hash); + if (this.IsEmptyNode) + { + writer.WriteVarBytes(Array.Empty()); + return; + } + writer.WriteVarBytes(Hash.ToArray()); } public override void DecodeSpecific(BinaryReader reader) { - Hash = reader.ReadVarBytes(); + var len = reader.ReadVarInt(); + if (len == 0) + { + Hash = null; + return; + } + if (len != UInt256.Length) throw new System.InvalidOperationException("Invalid hash bytes"); + Hash = new UInt256(reader.ReadBytes((int)len)); } public override JObject ToJson() { var json = new JObject(); - json["hash"] = Hash.ToHexString(); + json["hash"] = Hash.ToString(); return json; } } diff --git a/src/neo/Trie/MPT/MPTNode/LeafNode.cs b/src/neo/Trie/MPT/MPTNode/LeafNode.cs index b81455549f..1db2f28954 100644 --- a/src/neo/Trie/MPT/MPTNode/LeafNode.cs +++ b/src/neo/Trie/MPT/MPTNode/LeafNode.cs @@ -1,4 +1,3 @@ -using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; using System.IO; diff --git a/src/neo/Trie/MPT/MPTProofDb.cs b/src/neo/Trie/MPT/MPTProofStore.cs similarity index 100% rename from src/neo/Trie/MPT/MPTProofDb.cs rename to src/neo/Trie/MPT/MPTProofStore.cs index 284f547cc9..ea0b67c966 100644 --- a/src/neo/Trie/MPT/MPTProofDb.cs +++ b/src/neo/Trie/MPT/MPTProofStore.cs @@ -1,5 +1,5 @@ -using Neo.Persistence; using Neo.Cryptography; +using Neo.Persistence; using System.Collections.Generic; namespace Neo.Trie.MPT diff --git a/src/neo/Trie/MPT/MPTReadOnlyDb.cs b/src/neo/Trie/MPT/MPTReadOnlyDb.cs index 8c27d90c27..959fdc9044 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyDb.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyDb.cs @@ -1,3 +1,4 @@ +using Neo.IO; namespace Neo.Trie.MPT { @@ -10,9 +11,10 @@ public MPTReadOnlyDb(IKVReadOnlyStore store) this.store = store; } - public MPTNode Node(byte[] hash) + public MPTNode Node(UInt256 hash) { - var data = store.Get(hash); + if (hash is null) return null; + var data = store.Get(hash.ToArray()); return MPTNode.Decode(data); } } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index 09d8d04c84..985b2b641e 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -9,14 +9,14 @@ public class MPTReadOnlyTrie : IReadOnlyTrie private MPTReadOnlyDb rodb; protected MPTNode root; - public MPTReadOnlyTrie(byte[] root, IKVReadOnlyStore store) + public MPTReadOnlyTrie(UInt256 root, IKVReadOnlyStore store) { if (store is null) throw new System.ArgumentNullException(); this.rodb = new MPTReadOnlyDb(store); - if (root is null || root.Length == 0) + if (root is null) { this.root = HashNode.EmptyNode(); } @@ -80,7 +80,7 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) return false; } - public byte[] GetRoot() + public UInt256 GetRoot() { return root.GetHash(); } @@ -136,7 +136,7 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) return false; } - public static bool VerifyProof(byte[] root, byte[] path, HashSet proof, out byte[] value) + public static bool VerifyProof(UInt256 root, byte[] path, HashSet proof, out byte[] value) { var store = new MPTProofStore(proof); var trie = new MPTReadOnlyTrie(root, store); diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 1cecf67493..7aa02d24a2 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -7,7 +7,7 @@ public class MPTTrie : MPTReadOnlyTrie, ITrie { private MPTDb db; - public MPTTrie(byte[] root, IKVStore store) : base(root, store) + public MPTTrie(UInt256 root, IKVStore store) : base(root, store) { this.db = new MPTDb(store); } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs index 122e8be5e2..3b77f63246 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs @@ -23,5 +23,13 @@ public void TestFlag() var n = new ExtensionNode(); Assert.IsTrue(n.Dirty); } + + [TestMethod] + public void TestHashNode() + { + var hn = new HashNode(null); + var data = hn.Encode(); + Assert.AreEqual("0200", data.ToHexString()); + } } } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index d15351ed41..2564d575c6 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -36,7 +36,7 @@ public class UT_MPTTrie private MPTNode root; private IKVStore mptdb; - private byte[] rootHash; + private UInt256 rootHash; [TestInitialize] public void TestInit() @@ -126,7 +126,7 @@ public void TestTryPut() Assert.IsTrue(result); result = mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); Assert.IsTrue(result); - Assert.AreEqual(rootHash.ToHexString(), mpt.GetRoot().ToHexString()); + Assert.AreEqual(rootHash.ToString(), mpt.GetRoot().ToString()); } [TestMethod] @@ -156,8 +156,8 @@ public void TestTryDelete() b.Children[9] = l2; r1.Next = v1; - Assert.AreEqual("b3fba0ac4e39628963bc9cd348b230800a3e531e1c09d75e881e46e946aba3de", r1.GetHash().ToHexString()); - Assert.AreEqual("0dd6476b36e7d5251b9d8d8f4bf61b81cc3b270e3367ca2fd93df8e2ffe1e893", r.GetHash().ToHexString()); + Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.GetHash().ToString()); + Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.GetHash().ToString()); var mpt = new MPTTrie(rootHash, mptdb); var result = true; @@ -167,7 +167,7 @@ public void TestTryDelete() Assert.IsTrue(result); result = mpt.TryDelete("acae".HexToBytes()); Assert.IsTrue(result); - Assert.AreEqual("b3fba0ac4e39628963bc9cd348b230800a3e531e1c09d75e881e46e946aba3de", mpt.GetRoot().ToHexString()); + Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", mpt.GetRoot().ToString()); } [TestMethod] @@ -240,7 +240,7 @@ public void TestGetProof() b.Children[10] = l3; var mpt = new MPTTrie(rootHash, mptdb); - Assert.AreEqual(r.GetHash().ToHexString(), mpt.GetRoot().ToHexString()); + Assert.AreEqual(r.GetHash().ToString(), mpt.GetRoot().ToString()); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); Assert.AreEqual(4, proof.Count); From 23835e4701eba3c66370fb5ae09d28392336f4aa Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 26 Mar 2020 15:38:56 +0800 Subject: [PATCH 078/171] change name --- src/neo/Trie/IReadOnlyTrie.cs | 4 ++-- src/neo/Trie/ITrie.cs | 4 ++-- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 12 ++++++------ src/neo/Trie/MPT/MPTTrie.cs | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/neo/Trie/IReadOnlyTrie.cs b/src/neo/Trie/IReadOnlyTrie.cs index 57127955d3..ea014858ec 100644 --- a/src/neo/Trie/IReadOnlyTrie.cs +++ b/src/neo/Trie/IReadOnlyTrie.cs @@ -4,10 +4,10 @@ namespace Neo.Trie { public interface IReadOnlyTrie { - bool TryGet(byte[] path, out byte[] value); + bool TryGet(byte[] key, out byte[] value); UInt256 GetRoot(); - bool GetProof(byte[] path, out HashSet set); + bool GetProof(byte[] key, out HashSet set); } } diff --git a/src/neo/Trie/ITrie.cs b/src/neo/Trie/ITrie.cs index 07a384edd1..83ede6bafe 100644 --- a/src/neo/Trie/ITrie.cs +++ b/src/neo/Trie/ITrie.cs @@ -3,8 +3,8 @@ namespace Neo.Trie { public interface ITrie : IReadOnlyTrie { - bool Put(byte[] path, byte[] value); + bool Put(byte[] key, byte[] value); - bool TryDelete(byte[] path); + bool TryDelete(byte[] key); } } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index 985b2b641e..96fdd3dbf8 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -31,9 +31,9 @@ public MPTNode Resolve(HashNode hn) return rodb.Node(hn.Hash); } - public bool TryGet(byte[] path, out byte[] value) + public bool TryGet(byte[] key, out byte[] value) { - path = path.ToNibbles(); + var path = key.ToNibbles(); return TryGet(ref root, path, out value); } @@ -85,10 +85,10 @@ public UInt256 GetRoot() return root.GetHash(); } - public bool GetProof(byte[] path, out HashSet set) + public bool GetProof(byte[] key, out HashSet set) { set = new HashSet(ByteArrayEqualityComparer.Default); - path = path.ToNibbles(); + var path = key.ToNibbles(); return GetProof(ref root, path, set); } @@ -136,11 +136,11 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) return false; } - public static bool VerifyProof(UInt256 root, byte[] path, HashSet proof, out byte[] value) + public static bool VerifyProof(UInt256 root, byte[] key, HashSet proof, out byte[] value) { var store = new MPTProofStore(proof); var trie = new MPTReadOnlyTrie(root, store); - return trie.TryGet(path, out value); + return trie.TryGet(key, out value); } } } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 7aa02d24a2..a41727c9bf 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -12,9 +12,9 @@ public MPTTrie(UInt256 root, IKVStore store) : base(root, store) this.db = new MPTDb(store); } - public bool Put(byte[] path, byte[] value) + public bool Put(byte[] key, byte[] value) { - path = path.ToNibbles(); + var path = key.ToNibbles(); if (value.Length == 0) { return TryDelete(ref root, path); @@ -128,9 +128,9 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } } - public bool TryDelete(byte[] path) + public bool TryDelete(byte[] key) { - path = path.ToNibbles(); + var path = key.ToNibbles(); return TryDelete(ref root, path); } From 57b9dc69f1f1e0a68fa20b876f17492cf8d7d364 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 26 Mar 2020 16:42:55 +0800 Subject: [PATCH 079/171] modify ByteArray.Add --- src/neo/Helper.cs | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index 4272f02c7e..da4c27ad55 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -337,10 +337,28 @@ public static byte[] CommonPrefix(this byte[] a, byte[] b) public static bool Equal(this byte[] a, byte[] b) { - if (a.Length != b.Length) return false; - for (int i = 0; i < a.Length; i++) + if (a is null || b is null) return false; + var len = a.Length; + if (len != b.Length) return false; + unsafe { - if (a[i] != b[i]) return false; + fixed (byte* ap = a, bp = b) + { + long* alp = (long*)ap, blp = (long*)bp; + for (; len >= 8; len -= 8) + { + if (*alp != *blp) return false; + alp++; + blp++; + } + byte* abp = (byte*)alp, bbp = (byte*)blp; + for (; len > 0; len--) + { + if (*abp != *bbp) return false; + abp++; + bbp++; + } + } } return true; } From 6b72dd1a50d3feb2b84f0acb559fd7c396f38c85 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 31 Mar 2020 19:26:44 +0800 Subject: [PATCH 080/171] optimize take prefix --- src/neo/Helper.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index da4c27ad55..b2fd01e6ec 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -323,16 +323,15 @@ public static byte[] CommonPrefix(this byte[] a, byte[] b) { var prefix = Array.Empty(); var minLen = a.Length <= b.Length ? a.Length : b.Length; - + int i = 0; if (a.Length != 0 && b.Length != 0) { - for (int i = 0; i < minLen; i++) + for (i = 0; i < minLen; i++) { if (a[i] != b[i]) break; - prefix = prefix.Add(a[i]); } } - return prefix; + return a.Take(i).ToArray(); } public static bool Equal(this byte[] a, byte[] b) From f9a300a6241890ecf6f3dd0a557ffb97a83c379b Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 31 Mar 2020 20:03:31 +0800 Subject: [PATCH 081/171] optimize take prefix --- src/neo/Helper.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index b2fd01e6ec..a994f5f121 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -321,7 +321,6 @@ public static byte[] Concat(this byte[] a, byte[] b) public static byte[] CommonPrefix(this byte[] a, byte[] b) { - var prefix = Array.Empty(); var minLen = a.Length <= b.Length ? a.Length : b.Length; int i = 0; if (a.Length != 0 && b.Length != 0) @@ -331,7 +330,9 @@ public static byte[] CommonPrefix(this byte[] a, byte[] b) if (a[i] != b[i]) break; } } - return a.Take(i).ToArray(); + var prefix = new byte[i]; + Array.Copy(a, prefix, i); + return prefix; } public static bool Equal(this byte[] a, byte[] b) From 0261593f680ad74e46fbc6aee49a683af6e93a17 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 31 Mar 2020 20:46:49 +0800 Subject: [PATCH 082/171] add equal ut --- tests/neo.UnitTests/UT_Helper.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index 3376ce6a65..71bf98eebc 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -319,6 +319,23 @@ public void TestCommonPrefix() Assert.IsTrue(prefix.Length == 0); } + [TestMethod] + public void TestEqual() + { + var a = new byte[] { 1, 2, 3, 4, 5 }; + var b = new byte[] { 1, 2, 3, 4, 6 }; + var c = new byte[0]; + var d = new byte[] { 1, 2, 3, 4, 5 }; + var e = new byte[] { 1, 2, 3, 4, 5, 6, 7 }; + var f = new byte[] { 1 }; + + Assert.IsFalse(a.Equal(b)); + Assert.IsFalse(a.Equal(c)); + Assert.IsTrue(a.Equal(d)); + Assert.IsFalse(a.Equal(e)); + Assert.IsFalse(a.Equal(f)); + } + [TestMethod] public void TestToNibbles() { From 4b5459b1fd6987722eb36836b2a01ffa1fed84e3 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 1 Apr 2020 09:49:40 +0200 Subject: [PATCH 083/171] Simplify CommonPrefix --- src/neo/Helper.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index a994f5f121..18bc32e9e4 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -330,9 +330,7 @@ public static byte[] CommonPrefix(this byte[] a, byte[] b) if (a[i] != b[i]) break; } } - var prefix = new byte[i]; - Array.Copy(a, prefix, i); - return prefix; + return a[..i]; } public static bool Equal(this byte[] a, byte[] b) From dadb274d62292191084e88efcc80e85b3bdfa1eb Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 1 Apr 2020 16:42:36 +0800 Subject: [PATCH 084/171] add max key --- src/neo/Trie/MPT/MPTNode/ExtensionNode.cs | 1 + src/neo/Trie/MPT/MPTTrie.cs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs index 4f694cb0c7..26e56ecbb4 100644 --- a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs @@ -6,6 +6,7 @@ namespace Neo.Trie.MPT { public class ExtensionNode : MPTNode { + public const int MAX_KEY_LENGTH = UInt256.Length * 4; public byte[] Key; public MPTNode Next; diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index a41727c9bf..8f9667834d 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -15,10 +15,10 @@ public MPTTrie(UInt256 root, IKVStore store) : base(root, store) public bool Put(byte[] key, byte[] value) { var path = key.ToNibbles(); + if (ExtensionNode.MAX_KEY_LENGTH < path.Length) + return false; if (value.Length == 0) - { return TryDelete(ref root, path); - } var n = new LeafNode(value); return Put(ref root, path, n); } From 6204d0b22469d567e0bbf6f6fd5926784fc78416 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 1 Apr 2020 15:30:08 +0800 Subject: [PATCH 085/171] use itore --- src/neo/Trie/IKVReadOnlyStore.cs | 7 ---- src/neo/Trie/IKVStore.cs | 7 ---- src/neo/Trie/MPT/MPTDb.cs | 7 ++-- src/neo/Trie/MPT/MPTProofStore.cs | 16 +++++++- src/neo/Trie/MPT/MPTReadOnlyDb.cs | 10 +++-- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 6 +-- src/neo/Trie/MPT/MPTTrie.cs | 5 ++- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 44 ++++++---------------- 8 files changed, 41 insertions(+), 61 deletions(-) delete mode 100644 src/neo/Trie/IKVReadOnlyStore.cs delete mode 100644 src/neo/Trie/IKVStore.cs diff --git a/src/neo/Trie/IKVReadOnlyStore.cs b/src/neo/Trie/IKVReadOnlyStore.cs deleted file mode 100644 index 38e372f1e6..0000000000 --- a/src/neo/Trie/IKVReadOnlyStore.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Neo.Trie -{ - public interface IKVReadOnlyStore - { - byte[] Get(byte[] key); - } -} diff --git a/src/neo/Trie/IKVStore.cs b/src/neo/Trie/IKVStore.cs deleted file mode 100644 index d689457d78..0000000000 --- a/src/neo/Trie/IKVStore.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Neo.Trie -{ - public interface IKVStore : IKVReadOnlyStore - { - void Put(byte[] key, byte[] value); - } -} diff --git a/src/neo/Trie/MPT/MPTDb.cs b/src/neo/Trie/MPT/MPTDb.cs index 5a82c32b28..89e8238705 100644 --- a/src/neo/Trie/MPT/MPTDb.cs +++ b/src/neo/Trie/MPT/MPTDb.cs @@ -1,12 +1,13 @@ using Neo.IO; +using Neo.Persistence; namespace Neo.Trie.MPT { public class MPTDb : MPTReadOnlyDb { - private IKVStore store; + private IStore store; - public MPTDb(IKVStore store) : base(store) + public MPTDb(IStore store, byte prefix) : base(store, prefix) { this.store = store; } @@ -15,7 +16,7 @@ public void Put(MPTNode node) { if (node is HashNode hn) throw new System.InvalidOperationException("Means nothing to store HashNode"); - store.Put(node.GetHash().ToArray(), node.Encode()); + store.Put(prefix, node.GetHash().ToArray(), node.Encode()); } } } diff --git a/src/neo/Trie/MPT/MPTProofStore.cs b/src/neo/Trie/MPT/MPTProofStore.cs index ea0b67c966..55ad8e093b 100644 --- a/src/neo/Trie/MPT/MPTProofStore.cs +++ b/src/neo/Trie/MPT/MPTProofStore.cs @@ -1,10 +1,13 @@ using Neo.Cryptography; +using Neo.IO; using Neo.Persistence; +using System; using System.Collections.Generic; +using System.Linq; namespace Neo.Trie.MPT { - public class MPTProofStore : IKVReadOnlyStore + public class MPTProofStore : IReadOnlyStore { private Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); @@ -16,10 +19,19 @@ public MPTProofStore(HashSet proof) } } - public byte[] Get(byte[] hash) + public byte[] TryGet(byte prefix, byte[] hash) { var result = store.TryGetValue(hash, out byte[] value); return result ? value : null; } + + public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) + { + IEnumerable> records = store; + if (prefix?.Length > 0) + records = records.Where(p => p.Key.AsSpan().StartsWith(prefix)); + records = records.OrderBy(p => p.Key, ByteArrayComparer.Default); + return records.Select(p => (p.Key, p.Value)); + } } } diff --git a/src/neo/Trie/MPT/MPTReadOnlyDb.cs b/src/neo/Trie/MPT/MPTReadOnlyDb.cs index 959fdc9044..99553c896f 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyDb.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyDb.cs @@ -1,20 +1,22 @@ using Neo.IO; +using Neo.Persistence; namespace Neo.Trie.MPT { public class MPTReadOnlyDb { - private IKVReadOnlyStore store; - - public MPTReadOnlyDb(IKVReadOnlyStore store) + private IReadOnlyStore store; + protected byte prefix; + public MPTReadOnlyDb(IReadOnlyStore store, byte prefix) { this.store = store; + this.prefix = prefix; } public MPTNode Node(UInt256 hash) { if (hash is null) return null; - var data = store.Get(hash.ToArray()); + var data = store.TryGet(prefix, hash.ToArray()); return MPTNode.Decode(data); } } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index 96fdd3dbf8..cb7f0bd203 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -9,12 +9,12 @@ public class MPTReadOnlyTrie : IReadOnlyTrie private MPTReadOnlyDb rodb; protected MPTNode root; - public MPTReadOnlyTrie(UInt256 root, IKVReadOnlyStore store) + public MPTReadOnlyTrie(UInt256 root, IReadOnlyStore store, byte prefix) { if (store is null) throw new System.ArgumentNullException(); - this.rodb = new MPTReadOnlyDb(store); + this.rodb = new MPTReadOnlyDb(store, prefix); if (root is null) { @@ -139,7 +139,7 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) public static bool VerifyProof(UInt256 root, byte[] key, HashSet proof, out byte[] value) { var store = new MPTProofStore(proof); - var trie = new MPTReadOnlyTrie(root, store); + var trie = new MPTReadOnlyTrie(root, store, 0); return trie.TryGet(key, out value); } } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 8f9667834d..6af1bf436a 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,4 +1,5 @@ using Neo.IO.Json; +using Neo.Persistence; using System; namespace Neo.Trie.MPT @@ -7,9 +8,9 @@ public class MPTTrie : MPTReadOnlyTrie, ITrie { private MPTDb db; - public MPTTrie(UInt256 root, IKVStore store) : base(root, store) + public MPTTrie(UInt256 root, IStore store, byte prefix) : base(root, store, prefix) { - this.db = new MPTDb(store); + this.db = new MPTDb(store, prefix); } public bool Put(byte[] key, byte[] value) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 2564d575c6..42ba32c01a 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -8,33 +8,11 @@ namespace Neo.UnitTests.Trie.MPT { - class MemoryStore : IKVStore - { - private Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); - - public void Put(byte[] key, byte[] value) - { - store[key] = value; - } - - public void Delete(byte[] key) - { - store.Remove(key); - } - - public byte[] Get(byte[] key) - { - var result = store.TryGetValue(key, out byte[] value); - if (result) return value; - return Array.Empty(); - } - } - [TestClass] public class UT_MPTTrie { private MPTNode root; - private IKVStore mptdb; + private IStore mptdb; private UInt256 rootHash; @@ -68,7 +46,7 @@ public void TestInit() b.Children[10] = l3; root = r; var store = new MemoryStore(); - var db = new MPTDb(store); + var db = new MPTDb(store, 0); this.rootHash = root.GetHash(); db.Put(r); db.Put(b); @@ -84,7 +62,7 @@ public void TestInit() [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(rootHash, mptdb); + var mpt = new MPTTrie(rootHash, mptdb, 0); var result = mpt.TryGet("ac01".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.AreEqual("abcd", value.ToHexString()); @@ -109,7 +87,7 @@ public void TestTryGet() [TestMethod] public void TestTryGetResolve() { - var mpt = new MPTTrie(rootHash, mptdb); + var mpt = new MPTTrie(rootHash, mptdb, 0); var result = mpt.TryGet("acae".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.IsTrue(Encoding.ASCII.GetBytes("hello").Equal(value)); @@ -119,7 +97,7 @@ public void TestTryGetResolve() public void TestTryPut() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store); + var mpt = new MPTTrie(null, store, 0); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); @@ -159,7 +137,7 @@ public void TestTryDelete() Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.GetHash().ToString()); Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.GetHash().ToString()); - var mpt = new MPTTrie(rootHash, mptdb); + var mpt = new MPTTrie(rootHash, mptdb, 0); var result = true; result = mpt.TryGet("ac99".HexToBytes(), out byte[] value); Assert.IsTrue(result); @@ -174,7 +152,7 @@ public void TestTryDelete() public void TestDeleteSameValue() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store); + var mpt = new MPTTrie(null, store, 0); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes()); @@ -187,7 +165,7 @@ public void TestDeleteSameValue() result = mpt.TryGet("ac02".HexToBytes(), out value); Assert.IsTrue(result); - var mpt0 = new MPTTrie(mpt.GetRoot(), store); + var mpt0 = new MPTTrie(mpt.GetRoot(), store, 0); result = mpt0.TryGet("ac02".HexToBytes(), out value); Assert.IsTrue(result); } @@ -196,7 +174,7 @@ public void TestDeleteSameValue() public void TestBranchNodeRemainValue() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store); + var mpt = new MPTTrie(null, store, 0); var result = mpt.Put("ac11".HexToBytes(), "ac11".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac22".HexToBytes(), "ac22".HexToBytes()); @@ -239,7 +217,7 @@ public void TestGetProof() l2.Next = v2; b.Children[10] = l3; - var mpt = new MPTTrie(rootHash, mptdb); + var mpt = new MPTTrie(rootHash, mptdb, 0); Assert.AreEqual(r.GetHash().ToString(), mpt.GetRoot().ToString()); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); @@ -253,7 +231,7 @@ public void TestGetProof() [TestMethod] public void TestVerifyProof() { - var mpt = new MPTTrie(rootHash, mptdb); + var mpt = new MPTTrie(rootHash, mptdb, 0); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); result = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof, out byte[] value); From 7e4d12e301e2c4c7da38c5c5cabf46cdf4029611 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 1 Apr 2020 16:47:27 +0800 Subject: [PATCH 086/171] rm --- src/neo/Trie/IReadOnlyTrie.cs | 13 ------------- src/neo/Trie/ITrie.cs | 10 ---------- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 2 +- src/neo/Trie/MPT/MPTTrie.cs | 2 +- 4 files changed, 2 insertions(+), 25 deletions(-) delete mode 100644 src/neo/Trie/IReadOnlyTrie.cs delete mode 100644 src/neo/Trie/ITrie.cs diff --git a/src/neo/Trie/IReadOnlyTrie.cs b/src/neo/Trie/IReadOnlyTrie.cs deleted file mode 100644 index ea014858ec..0000000000 --- a/src/neo/Trie/IReadOnlyTrie.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; - -namespace Neo.Trie -{ - public interface IReadOnlyTrie - { - bool TryGet(byte[] key, out byte[] value); - - UInt256 GetRoot(); - - bool GetProof(byte[] key, out HashSet set); - } -} diff --git a/src/neo/Trie/ITrie.cs b/src/neo/Trie/ITrie.cs deleted file mode 100644 index 83ede6bafe..0000000000 --- a/src/neo/Trie/ITrie.cs +++ /dev/null @@ -1,10 +0,0 @@ - -namespace Neo.Trie -{ - public interface ITrie : IReadOnlyTrie - { - bool Put(byte[] key, byte[] value); - - bool TryDelete(byte[] key); - } -} diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index cb7f0bd203..724a63eb60 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -4,7 +4,7 @@ namespace Neo.Trie.MPT { - public class MPTReadOnlyTrie : IReadOnlyTrie + public class MPTReadOnlyTrie { private MPTReadOnlyDb rodb; protected MPTNode root; diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 6af1bf436a..de35b86898 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -4,7 +4,7 @@ namespace Neo.Trie.MPT { - public class MPTTrie : MPTReadOnlyTrie, ITrie + public class MPTTrie : MPTReadOnlyTrie { private MPTDb db; From cca3fbdb7f0de25560bfde99614400472cc94959 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 1 Apr 2020 16:57:16 +0800 Subject: [PATCH 087/171] fix max length --- src/neo/Trie/MPT/MPTNode/ExtensionNode.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs index 26e56ecbb4..929fcba436 100644 --- a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs @@ -1,12 +1,13 @@ using Neo.IO; using Neo.IO.Json; +using Neo.SmartContract; using System.IO; namespace Neo.Trie.MPT { public class ExtensionNode : MPTNode { - public const int MAX_KEY_LENGTH = UInt256.Length * 4; + public const int MAX_KEY_LENGTH = (InteropService.Storage.MaxKeySize + sizeof(int) + InteropService.Storage.MaxKeySize / IO.Helper.GroupingSizeInBytes) * 2; public byte[] Key; public MPTNode Next; From b0ffd64659a98646567d4f050b3d49ab8e884d55 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 1 Apr 2020 17:03:25 +0800 Subject: [PATCH 088/171] use ISnapshot and fix ut --- src/neo/Trie/MPT/MPTDb.cs | 4 ++-- src/neo/Trie/MPT/MPTTrie.cs | 2 +- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 24 +++++++++++++--------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/neo/Trie/MPT/MPTDb.cs b/src/neo/Trie/MPT/MPTDb.cs index 89e8238705..d4adac8747 100644 --- a/src/neo/Trie/MPT/MPTDb.cs +++ b/src/neo/Trie/MPT/MPTDb.cs @@ -5,9 +5,9 @@ namespace Neo.Trie.MPT { public class MPTDb : MPTReadOnlyDb { - private IStore store; + private ISnapshot store; - public MPTDb(IStore store, byte prefix) : base(store, prefix) + public MPTDb(ISnapshot store, byte prefix) : base(store, prefix) { this.store = store; } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index de35b86898..a59a8b83c2 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -8,7 +8,7 @@ public class MPTTrie : MPTReadOnlyTrie { private MPTDb db; - public MPTTrie(UInt256 root, IStore store, byte prefix) : base(root, store, prefix) + public MPTTrie(UInt256 root, ISnapshot store, byte prefix) : base(root, store, prefix) { this.db = new MPTDb(store, prefix); } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 42ba32c01a..1dfa538bdc 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -46,7 +46,8 @@ public void TestInit() b.Children[10] = l3; root = r; var store = new MemoryStore(); - var db = new MPTDb(store, 0); + var snapshot = store.GetSnapshot(); + var db = new MPTDb(snapshot, 0); this.rootHash = root.GetHash(); db.Put(r); db.Put(b); @@ -56,13 +57,14 @@ public void TestInit() db.Put(v1); db.Put(v2); db.Put(v3); + snapshot.Commit(); this.mptdb = store; } [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(rootHash, mptdb, 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); var result = mpt.TryGet("ac01".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.AreEqual("abcd", value.ToHexString()); @@ -87,7 +89,7 @@ public void TestTryGet() [TestMethod] public void TestTryGetResolve() { - var mpt = new MPTTrie(rootHash, mptdb, 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); var result = mpt.TryGet("acae".HexToBytes(), out byte[] value); Assert.IsTrue(result); Assert.IsTrue(Encoding.ASCII.GetBytes("hello").Equal(value)); @@ -97,7 +99,7 @@ public void TestTryGetResolve() public void TestTryPut() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store, 0); + var mpt = new MPTTrie(null, store.GetSnapshot(), 0); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); @@ -137,7 +139,7 @@ public void TestTryDelete() Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.GetHash().ToString()); Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.GetHash().ToString()); - var mpt = new MPTTrie(rootHash, mptdb, 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); var result = true; result = mpt.TryGet("ac99".HexToBytes(), out byte[] value); Assert.IsTrue(result); @@ -152,7 +154,8 @@ public void TestTryDelete() public void TestDeleteSameValue() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store, 0); + var snapshot = store.GetSnapshot(); + var mpt = new MPTTrie(null, snapshot, 0); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes()); @@ -164,8 +167,9 @@ public void TestDeleteSameValue() result = mpt.TryDelete("ac01".HexToBytes()); result = mpt.TryGet("ac02".HexToBytes(), out value); Assert.IsTrue(result); + snapshot.Commit(); - var mpt0 = new MPTTrie(mpt.GetRoot(), store, 0); + var mpt0 = new MPTTrie(mpt.GetRoot(), store.GetSnapshot(), 0); result = mpt0.TryGet("ac02".HexToBytes(), out value); Assert.IsTrue(result); } @@ -174,7 +178,7 @@ public void TestDeleteSameValue() public void TestBranchNodeRemainValue() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store, 0); + var mpt = new MPTTrie(null, store.GetSnapshot(), 0); var result = mpt.Put("ac11".HexToBytes(), "ac11".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac22".HexToBytes(), "ac22".HexToBytes()); @@ -217,7 +221,7 @@ public void TestGetProof() l2.Next = v2; b.Children[10] = l3; - var mpt = new MPTTrie(rootHash, mptdb, 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); Assert.AreEqual(r.GetHash().ToString(), mpt.GetRoot().ToString()); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); @@ -231,7 +235,7 @@ public void TestGetProof() [TestMethod] public void TestVerifyProof() { - var mpt = new MPTTrie(rootHash, mptdb, 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); result = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof, out byte[] value); From 4002dec4816d3f6ce03110c5f578159266fe6b9e Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 1 Apr 2020 17:54:44 +0800 Subject: [PATCH 089/171] format --- src/neo/Trie/MPT/MPTReadOnlyDb.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/neo/Trie/MPT/MPTReadOnlyDb.cs b/src/neo/Trie/MPT/MPTReadOnlyDb.cs index 99553c896f..77ea99b039 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyDb.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyDb.cs @@ -7,6 +7,7 @@ public class MPTReadOnlyDb { private IReadOnlyStore store; protected byte prefix; + public MPTReadOnlyDb(IReadOnlyStore store, byte prefix) { this.store = store; From 2c11831b49e9883ce1d0d4d8db156d5d6d055ac4 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 1 Apr 2020 15:26:37 +0200 Subject: [PATCH 090/171] Optimize Skip --- src/neo/Helper.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index 18bc32e9e4..92d52d5f62 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -363,14 +363,12 @@ public static bool Equal(this byte[] a, byte[] b) public static byte[] Skip(this byte[] a, int count) { - var result = Array.Empty(); var len = a.Length - count; - if (0 < len) + if (len > 0) { - result = new byte[len]; - Array.Copy(a, count, result, 0, len); + return a[count..]; } - return result; + return Array.Empty(); } public static byte[] Add(this byte[] a, byte b) From ae64dccbc33aafd4cb9bbc617ea4e0df6e858dba Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 1 Apr 2020 15:28:23 +0200 Subject: [PATCH 091/171] Clean file --- src/neo/Trie/MPT/MPTNode.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 9cbd3bd343..cc3ae6b1bb 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -5,7 +5,6 @@ namespace Neo.Trie.MPT { - public abstract class MPTNode { private UInt256 hash; From 8ba4e68f06608ce48b457944162bd04c47685c86 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 2 Apr 2020 10:12:20 +0800 Subject: [PATCH 092/171] change property name --- src/neo/Trie/MPT/MPTNode/BranchNode.cs | 6 +++--- src/neo/Trie/MPT/MPTNode/ExtensionNode.cs | 2 +- src/neo/Trie/MPT/MPTTrie.cs | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/BranchNode.cs b/src/neo/Trie/MPT/MPTNode/BranchNode.cs index 25a7387996..9808d33dda 100644 --- a/src/neo/Trie/MPT/MPTNode/BranchNode.cs +++ b/src/neo/Trie/MPT/MPTNode/BranchNode.cs @@ -5,8 +5,8 @@ namespace Neo.Trie.MPT { public class BranchNode : MPTNode { - public const int CHILD_COUNT = 17; - public MPTNode[] Children = new MPTNode[CHILD_COUNT]; + public const int ChildCount = 17; + public MPTNode[] Children = new MPTNode[ChildCount]; public BranchNode() { @@ -39,7 +39,7 @@ public override void DecodeSpecific(BinaryReader reader) public override JObject ToJson() { var jarr = new JArray(); - for (int i = 0; i < CHILD_COUNT; i++) + for (int i = 0; i < ChildCount; i++) { jarr.Add(Children[i].ToJson()); } diff --git a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs index 929fcba436..33e2df8b75 100644 --- a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs @@ -7,7 +7,7 @@ namespace Neo.Trie.MPT { public class ExtensionNode : MPTNode { - public const int MAX_KEY_LENGTH = (InteropService.Storage.MaxKeySize + sizeof(int) + InteropService.Storage.MaxKeySize / IO.Helper.GroupingSizeInBytes) * 2; + public const int MaxKeyLength = (InteropService.Storage.MaxKeySize + sizeof(int) + InteropService.Storage.MaxKeySize / IO.Helper.GroupingSizeInBytes) * 2; public byte[] Key; public MPTNode Next; diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index a59a8b83c2..5ea940f62a 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -16,7 +16,7 @@ public MPTTrie(UInt256 root, ISnapshot store, byte prefix) : base(root, store, p public bool Put(byte[] key, byte[] value) { var path = key.ToNibbles(); - if (ExtensionNode.MAX_KEY_LENGTH < path.Length) + if (ExtensionNode.MaxKeyLength < path.Length) return false; if (value.Length == 0) return TryDelete(ref root, path); @@ -64,7 +64,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) if (pathRemain.Length == 0) { Put(ref grandSon2, pathRemain, val); - son.Children[BranchNode.CHILD_COUNT - 1] = grandSon2; + son.Children[BranchNode.ChildCount - 1] = grandSon2; } else { @@ -93,7 +93,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) var result = false; if (path.Length == 0) { - result = Put(ref branchNode.Children[BranchNode.CHILD_COUNT - 1], path, val); + result = Put(ref branchNode.Children[BranchNode.ChildCount - 1], path, val); } else { @@ -176,7 +176,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) var result = false; if (path.Length == 0) { - result = TryDelete(ref branchNode.Children[BranchNode.CHILD_COUNT - 1], path); + result = TryDelete(ref branchNode.Children[BranchNode.ChildCount - 1], path); } else { @@ -184,7 +184,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) } if (!result) return false; var childrenIndexes = Array.Empty(); - for (int i = 0; i < BranchNode.CHILD_COUNT; i++) + for (int i = 0; i < BranchNode.ChildCount; i++) { if (branchNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; childrenIndexes = childrenIndexes.Add((byte)i); @@ -197,7 +197,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) } var lastChildIndex = childrenIndexes[0]; var lastChild = branchNode.Children[lastChildIndex]; - if (lastChildIndex == BranchNode.CHILD_COUNT - 1) + if (lastChildIndex == BranchNode.ChildCount - 1) { node = lastChild; return true; From f6b8d33c2a19d5f7047533c42834123e0b969297 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 2 Apr 2020 10:39:18 +0800 Subject: [PATCH 093/171] add null check --- src/neo/Helper.cs | 8 +++++++- tests/neo.UnitTests/UT_Helper.cs | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index 92d52d5f62..ad4a68dcea 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -313,6 +313,9 @@ internal static IEnumerable WeightedFilter(this IList so public static byte[] Concat(this byte[] a, byte[] b) { + if (a is null && b is null) return Array.Empty(); + if (a is null) return b; + if (b is null) return a; var result = new byte[a.Length + b.Length]; a.CopyTo(result, 0); b.CopyTo(result, a.Length); @@ -321,6 +324,7 @@ public static byte[] Concat(this byte[] a, byte[] b) public static byte[] CommonPrefix(this byte[] a, byte[] b) { + if (a is null || b is null) return Array.Empty(); var minLen = a.Length <= b.Length ? a.Length : b.Length; int i = 0; if (a.Length != 0 && b.Length != 0) @@ -363,6 +367,7 @@ public static bool Equal(this byte[] a, byte[] b) public static byte[] Skip(this byte[] a, int count) { + if (a is null) return Array.Empty(); var len = a.Length - count; if (len > 0) { @@ -373,7 +378,7 @@ public static byte[] Skip(this byte[] a, int count) public static byte[] Add(this byte[] a, byte b) { - var len = a.Length; + var len = a is null ? 0 : a.Length; Array.Resize(ref a, len + 1); a[len] = b; return a; @@ -381,6 +386,7 @@ public static byte[] Add(this byte[] a, byte b) public static byte[] ToNibbles(this byte[] path) { + if (path is null) return Array.Empty(); var result = new byte[path.Length * 2]; for (int i = 0; i < path.Length; i++) { diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index 71bf98eebc..59f1573f01 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -263,6 +263,12 @@ public void TestConcat() var b = new byte[] { 0x02 }; a = a.Concat(b); Assert.AreEqual(2, a.Length); + + a = null; + Assert.AreEqual(b, a.Concat(b)); + + b = null; + Assert.AreEqual(0, a.Concat(b).Length); } [TestMethod] @@ -277,6 +283,9 @@ public void TestAdd() a = a.Add(b); Assert.AreEqual(1, a.Length); Assert.AreEqual("0c", a.ToHexString()); + + a = null; + Assert.AreEqual("0c", a.Add(b).ToHexString()); } [TestMethod] @@ -291,6 +300,9 @@ public void TestSkip() Assert.AreEqual(0, s.Length); s = s.Skip(2); Assert.AreEqual(0, s.Length); + + s = null; + Assert.AreEqual(0, s.Skip(1).Length); } [TestMethod] @@ -317,6 +329,14 @@ public void TestCommonPrefix() b = new byte[0]; prefix = a.CommonPrefix(b); Assert.IsTrue(prefix.Length == 0); + + a = "1234abcd".HexToBytes(); + b = null; + Assert.AreEqual(0, a.CommonPrefix(b).Length); + + a = null; + b = null; + Assert.AreEqual(0, a.CommonPrefix(b).Length); } [TestMethod] @@ -328,12 +348,14 @@ public void TestEqual() var d = new byte[] { 1, 2, 3, 4, 5 }; var e = new byte[] { 1, 2, 3, 4, 5, 6, 7 }; var f = new byte[] { 1 }; + byte[] g = null; Assert.IsFalse(a.Equal(b)); Assert.IsFalse(a.Equal(c)); Assert.IsTrue(a.Equal(d)); Assert.IsFalse(a.Equal(e)); Assert.IsFalse(a.Equal(f)); + Assert.IsFalse(a.Equal(g)); } [TestMethod] @@ -342,6 +364,9 @@ public void TestToNibbles() var a = "1234abcd".HexToBytes(); var n = a.ToNibbles(); Assert.AreEqual("010203040a0b0c0d", n.ToHexString()); + + a = null; + Assert.AreEqual(0, a.ToNibbles().Length); } } From 9c8d09c88ab7773de880a811aa3e7b4455411d30 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 2 Apr 2020 11:30:30 +0800 Subject: [PATCH 094/171] rm linq --- src/neo/Trie/MPT/MPTProofStore.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/neo/Trie/MPT/MPTProofStore.cs b/src/neo/Trie/MPT/MPTProofStore.cs index 55ad8e093b..d11513001e 100644 --- a/src/neo/Trie/MPT/MPTProofStore.cs +++ b/src/neo/Trie/MPT/MPTProofStore.cs @@ -27,11 +27,13 @@ public byte[] TryGet(byte prefix, byte[] hash) public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) { - IEnumerable> records = store; - if (prefix?.Length > 0) - records = records.Where(p => p.Key.AsSpan().StartsWith(prefix)); - records = records.OrderBy(p => p.Key, ByteArrayComparer.Default); - return records.Select(p => (p.Key, p.Value)); + foreach (var pair in store) + { + if (prefix is null || pair.Key.CommonPrefix(prefix).Length == prefix.Length) + { + yield return (pair.Key, pair.Value); + } + } } } } From 7a73e442a09252579719932037ca36508ad32f87 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 9 Apr 2020 11:29:22 +0800 Subject: [PATCH 095/171] concat --- src/neo/Helper.cs | 11 ----------- src/neo/Trie/MPT/MPTTrie.cs | 5 +++-- tests/neo.UnitTests/UT_Helper.cs | 15 --------------- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index ad4a68dcea..1f6bf028a8 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -311,17 +311,6 @@ internal static IEnumerable WeightedFilter(this IList so } } - public static byte[] Concat(this byte[] a, byte[] b) - { - if (a is null && b is null) return Array.Empty(); - if (a is null) return b; - if (b is null) return a; - var result = new byte[a.Length + b.Length]; - a.CopyTo(result, 0); - b.CopyTo(result, a.Length); - return result; - } - public static byte[] CommonPrefix(this byte[] a, byte[] b) { if (a is null || b is null) return Array.Empty(); diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 5ea940f62a..94f0724a0c 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,5 +1,6 @@ using Neo.IO.Json; using Neo.Persistence; +using static Neo.Helper; using System; namespace Neo.Trie.MPT @@ -162,7 +163,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) } if (extensionNode.Next is ExtensionNode sn) { - extensionNode.Key = extensionNode.Key.Concat(sn.Key); + extensionNode.Key = Concat(extensionNode.Key, sn.Key); extensionNode.Next = sn.Next; } extensionNode.SetDirty(); @@ -209,7 +210,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) } if (lastChild is ExtensionNode exNode) { - exNode.Key = childrenIndexes.Concat(exNode.Key); + exNode.Key = Concat(childrenIndexes, exNode.Key); exNode.SetDirty(); db.Put(exNode); node = exNode; diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index 59f1573f01..ff1db4662e 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -256,21 +256,6 @@ private static void CheckArgumentNullException(double start, double end, Func(); } - [TestMethod] - public void TestConcat() - { - var a = new byte[] { 0x01 }; - var b = new byte[] { 0x02 }; - a = a.Concat(b); - Assert.AreEqual(2, a.Length); - - a = null; - Assert.AreEqual(b, a.Concat(b)); - - b = null; - Assert.AreEqual(0, a.Concat(b).Length); - } - [TestMethod] public void TestAdd() { From 5a8b5f17e4c27e03c4628d117577b477c2edcb09 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 9 Apr 2020 18:03:47 +0800 Subject: [PATCH 096/171] use SequenceEqual --- src/neo/Helper.cs | 28 ---------------------- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 2 +- tests/neo.UnitTests/UT_Helper.cs | 19 --------------- 3 files changed, 1 insertion(+), 48 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index 1f6bf028a8..00dae35dec 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -326,34 +326,6 @@ public static byte[] CommonPrefix(this byte[] a, byte[] b) return a[..i]; } - public static bool Equal(this byte[] a, byte[] b) - { - if (a is null || b is null) return false; - var len = a.Length; - if (len != b.Length) return false; - unsafe - { - fixed (byte* ap = a, bp = b) - { - long* alp = (long*)ap, blp = (long*)bp; - for (; len >= 8; len -= 8) - { - if (*alp != *blp) return false; - alp++; - blp++; - } - byte* abp = (byte*)alp, bbp = (byte*)blp; - for (; len > 0; len--) - { - if (*abp != *bbp) return false; - abp++; - bbp++; - } - } - } - return true; - } - public static byte[] Skip(this byte[] a, int count) { if (a is null) return Array.Empty(); diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 1dfa538bdc..81332a7583 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -92,7 +92,7 @@ public void TestTryGetResolve() var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); var result = mpt.TryGet("acae".HexToBytes(), out byte[] value); Assert.IsTrue(result); - Assert.IsTrue(Encoding.ASCII.GetBytes("hello").Equal(value)); + Assert.IsTrue(MemoryExtensions.SequenceEqual(Encoding.ASCII.GetBytes("hello"), (value))); } [TestMethod] diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index ff1db4662e..29f862361f 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -324,25 +324,6 @@ public void TestCommonPrefix() Assert.AreEqual(0, a.CommonPrefix(b).Length); } - [TestMethod] - public void TestEqual() - { - var a = new byte[] { 1, 2, 3, 4, 5 }; - var b = new byte[] { 1, 2, 3, 4, 6 }; - var c = new byte[0]; - var d = new byte[] { 1, 2, 3, 4, 5 }; - var e = new byte[] { 1, 2, 3, 4, 5, 6, 7 }; - var f = new byte[] { 1 }; - byte[] g = null; - - Assert.IsFalse(a.Equal(b)); - Assert.IsFalse(a.Equal(c)); - Assert.IsTrue(a.Equal(d)); - Assert.IsFalse(a.Equal(e)); - Assert.IsFalse(a.Equal(f)); - Assert.IsFalse(a.Equal(g)); - } - [TestMethod] public void TestToNibbles() { From 96ffeb002fff4d287d203fe88847486e02d260a2 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 9 Apr 2020 18:55:29 +0800 Subject: [PATCH 097/171] fix double dispose stream --- src/neo/Trie/MPT/MPTNode.cs | 80 +++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index cc3ae6b1bb..3ca802e058 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -36,13 +36,22 @@ public MPTNode() public byte[] Encode() { - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) + MemoryStream ms = null; + try { - writer.Write((byte)nType); - EncodeSpecific(writer); - writer.Flush(); - return ms.ToArray(); + ms = new MemoryStream(); + using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) + { + writer.Write((byte)nType); + EncodeSpecific(writer); + writer.Flush(); + return ms.ToArray(); + } + } + finally + { + if (ms != null) + ms.Dispose(); } } @@ -52,35 +61,44 @@ public static MPTNode Decode(byte[] data) { if (data is null || data.Length == 0) return null; - - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) + MemoryStream ms = null; + try { - var nodeType = (NodeType)reader.ReadByte(); - switch (nodeType) + ms = new MemoryStream(data, false); + using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) { - case NodeType.BranchNode: - { - var n = new BranchNode(); - n.DecodeSpecific(reader); - return n; - } - case NodeType.ExtensionNode: - { - var n = new ExtensionNode(); - n.DecodeSpecific(reader); - return n; - } - case NodeType.LeafNode: - { - var n = new LeafNode(); - n.DecodeSpecific(reader); - return n; - } - default: - throw new System.InvalidOperationException(); + var nodeType = (NodeType)reader.ReadByte(); + switch (nodeType) + { + case NodeType.BranchNode: + { + var n = new BranchNode(); + n.DecodeSpecific(reader); + return n; + } + case NodeType.ExtensionNode: + { + var n = new ExtensionNode(); + n.DecodeSpecific(reader); + return n; + } + case NodeType.LeafNode: + { + var n = new LeafNode(); + n.DecodeSpecific(reader); + return n; + } + default: + throw new System.InvalidOperationException(); + } } } + finally + { + if (ms != null) + ms.Dispose(); + } + } public abstract void DecodeSpecific(BinaryReader reader); From e17a4818d90aa2c5e4dce1fb8fdd8b8b86ccd6a7 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 10 Apr 2020 15:06:47 +0800 Subject: [PATCH 098/171] use start with --- src/neo/Trie/MPT/MPTProofStore.cs | 4 +--- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 10 ++++------ src/neo/Trie/MPT/MPTTrie.cs | 5 ++--- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/neo/Trie/MPT/MPTProofStore.cs b/src/neo/Trie/MPT/MPTProofStore.cs index d11513001e..a237e97dfc 100644 --- a/src/neo/Trie/MPT/MPTProofStore.cs +++ b/src/neo/Trie/MPT/MPTProofStore.cs @@ -1,9 +1,7 @@ using Neo.Cryptography; -using Neo.IO; using Neo.Persistence; using System; using System.Collections.Generic; -using System.Linq; namespace Neo.Trie.MPT { @@ -29,7 +27,7 @@ public byte[] TryGet(byte prefix, byte[] hash) { foreach (var pair in store) { - if (prefix is null || pair.Key.CommonPrefix(prefix).Length == prefix.Length) + if (prefix is null || pair.Key.AsSpan().StartsWith(prefix)) { yield return (pair.Key, pair.Value); } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index 724a63eb60..f901085a54 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -68,10 +68,9 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) } case ExtensionNode extensionNode: { - var prefix = extensionNode.Key.CommonPrefix(path); - if (prefix.Length == extensionNode.Key.Length) + if (path.AsSpan().StartsWith(extensionNode.Key)) { - return TryGet(ref extensionNode.Next, path.Skip(prefix.Length), out value); + return TryGet(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), out value); } break; } @@ -124,11 +123,10 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) } case ExtensionNode extensionNode: { - var prefix = extensionNode.Key.CommonPrefix(path); - if (prefix.Length == extensionNode.Key.Length) + if (path.AsSpan().StartsWith(extensionNode.Key)) { set.Add(extensionNode.Encode()); - return GetProof(ref extensionNode.Next, path.Skip(prefix.Length), set); + return GetProof(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), set); } break; } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 94f0724a0c..58cef0b359 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -151,10 +151,9 @@ private bool TryDelete(ref MPTNode node, byte[] path) } case ExtensionNode extensionNode: { - var prefix = extensionNode.Key.CommonPrefix(path); - if (prefix.Length == extensionNode.Key.Length) + if (path.AsSpan().StartsWith(extensionNode.Key)) { - var result = TryDelete(ref extensionNode.Next, path.Skip(prefix.Length)); + var result = TryDelete(ref extensionNode.Next, path.Skip(extensionNode.Key.Length)); if (!result) return false; if (extensionNode.Next is HashNode hashNode && hashNode.IsEmptyNode) { From 4fa4015198ff7ed971e076b1a0dc8a86bb4f4a4a Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 10 Apr 2020 16:53:01 +0800 Subject: [PATCH 099/171] fix some --- src/neo/Trie/MPT/MPTNode.cs | 1 - src/neo/Trie/MPT/MPTNode/BranchNode.cs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 3ca802e058..312efea929 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -98,7 +98,6 @@ public static MPTNode Decode(byte[] data) if (ms != null) ms.Dispose(); } - } public abstract void DecodeSpecific(BinaryReader reader); diff --git a/src/neo/Trie/MPT/MPTNode/BranchNode.cs b/src/neo/Trie/MPT/MPTNode/BranchNode.cs index 9808d33dda..298839c5dc 100644 --- a/src/neo/Trie/MPT/MPTNode/BranchNode.cs +++ b/src/neo/Trie/MPT/MPTNode/BranchNode.cs @@ -11,7 +11,7 @@ public class BranchNode : MPTNode public BranchNode() { nType = NodeType.BranchNode; - for (int i = 0; i < Children.Length; i++) + for (int i = 0; i < ChildCount; i++) { Children[i] = HashNode.EmptyNode(); } @@ -19,7 +19,7 @@ public BranchNode() public override void EncodeSpecific(BinaryWriter writer) { - for (int i = 0; i < Children.Length; i++) + for (int i = 0; i < ChildCount; i++) { var hashNode = new HashNode(Children[i].GetHash()); hashNode.EncodeSpecific(writer); @@ -28,7 +28,7 @@ public override void EncodeSpecific(BinaryWriter writer) public override void DecodeSpecific(BinaryReader reader) { - for (int i = 0; i < Children.Length; i++) + for (int i = 0; i < ChildCount; i++) { var hashNode = new HashNode(); hashNode.DecodeSpecific(reader); From 0e5747bc6d51373ffad755c456b2c2b320d00b0c Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 10 Apr 2020 17:17:22 +0800 Subject: [PATCH 100/171] use startswith --- src/neo/Trie/MPT/MPTTrie.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 58cef0b359..89125f735b 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -41,10 +41,9 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } case ExtensionNode extensionNode: { - var prefix = extensionNode.Key.CommonPrefix(path); - if (prefix.Length == extensionNode.Key.Length) + if (path.AsSpan().StartsWith(extensionNode.Key)) { - var result = Put(ref extensionNode.Next, path.Skip(prefix.Length), val); + var result = Put(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), val); if (result) { extensionNode.SetDirty(); @@ -52,7 +51,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } return result; } - + var prefix = extensionNode.Key.CommonPrefix(path); var pathRemain = path.Skip(prefix.Length); var keyRemain = extensionNode.Key.Skip(prefix.Length); var son = new BranchNode(); From cebeabeb9055ce7c303a0507108d4bf029968fb9 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 10 Apr 2020 17:44:00 +0800 Subject: [PATCH 101/171] Remove Helper.Skip() --- src/neo/Helper.cs | 11 ----------- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 8 ++++---- src/neo/Trie/MPT/MPTTrie.cs | 22 +++++++++++----------- tests/neo.UnitTests/UT_Helper.cs | 17 ----------------- 4 files changed, 15 insertions(+), 43 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index 00dae35dec..d67ac1573a 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -326,17 +326,6 @@ public static byte[] CommonPrefix(this byte[] a, byte[] b) return a[..i]; } - public static byte[] Skip(this byte[] a, int count) - { - if (a is null) return Array.Empty(); - var len = a.Length - count; - if (len > 0) - { - return a[count..]; - } - return Array.Empty(); - } - public static byte[] Add(this byte[] a, byte b) { var len = a is null ? 0 : a.Length; diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index f901085a54..0e72df90b4 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -64,13 +64,13 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) { return TryGet(ref branchNode.Children[16], path, out value); } - return TryGet(ref branchNode.Children[path[0]], path.Skip(1), out value); + return TryGet(ref branchNode.Children[path[0]], path[1..], out value); } case ExtensionNode extensionNode: { if (path.AsSpan().StartsWith(extensionNode.Key)) { - return TryGet(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), out value); + return TryGet(ref extensionNode.Next, path[extensionNode.Key.Length..], out value); } break; } @@ -119,14 +119,14 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) { return GetProof(ref branchNode.Children[16], path, set); } - return GetProof(ref branchNode.Children[path[0]], path.Skip(1), set); + return GetProof(ref branchNode.Children[path[0]], path[1..], set); } case ExtensionNode extensionNode: { if (path.AsSpan().StartsWith(extensionNode.Key)) { set.Add(extensionNode.Encode()); - return GetProof(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), set); + return GetProof(ref extensionNode.Next, path[extensionNode.Key.Length..], set); } break; } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 89125f735b..55d8266768 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,7 +1,7 @@ using Neo.IO.Json; using Neo.Persistence; -using static Neo.Helper; using System; +using static Neo.Helper; namespace Neo.Trie.MPT { @@ -43,7 +43,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { if (path.AsSpan().StartsWith(extensionNode.Key)) { - var result = Put(ref extensionNode.Next, path.Skip(extensionNode.Key.Length), val); + var result = Put(ref extensionNode.Next, path[extensionNode.Key.Length..], val); if (result) { extensionNode.SetDirty(); @@ -52,13 +52,13 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) return result; } var prefix = extensionNode.Key.CommonPrefix(path); - var pathRemain = path.Skip(prefix.Length); - var keyRemain = extensionNode.Key.Skip(prefix.Length); + var pathRemain = path[prefix.Length..]; + var keyRemain = extensionNode.Key[prefix.Length..]; var son = new BranchNode(); MPTNode grandSon1 = HashNode.EmptyNode(); MPTNode grandSon2 = HashNode.EmptyNode(); - Put(ref grandSon1, keyRemain.Skip(1), extensionNode.Next); + Put(ref grandSon1, keyRemain[1..], extensionNode.Next); son.Children[keyRemain[0]] = grandSon1; if (pathRemain.Length == 0) @@ -68,7 +68,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } else { - Put(ref grandSon2, pathRemain.Skip(1), val); + Put(ref grandSon2, pathRemain[1..], val); son.Children[pathRemain[0]] = grandSon2; } db.Put(son); @@ -90,14 +90,14 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } case BranchNode branchNode: { - var result = false; + bool result; if (path.Length == 0) { result = Put(ref branchNode.Children[BranchNode.ChildCount - 1], path, val); } else { - result = Put(ref branchNode.Children[path[0]], path.Skip(1), val); + result = Put(ref branchNode.Children[path[0]], path[1..], val); } if (result) { @@ -152,7 +152,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) { if (path.AsSpan().StartsWith(extensionNode.Key)) { - var result = TryDelete(ref extensionNode.Next, path.Skip(extensionNode.Key.Length)); + var result = TryDelete(ref extensionNode.Next, path[extensionNode.Key.Length..]); if (!result) return false; if (extensionNode.Next is HashNode hashNode && hashNode.IsEmptyNode) { @@ -172,14 +172,14 @@ private bool TryDelete(ref MPTNode node, byte[] path) } case BranchNode branchNode: { - var result = false; + bool result; if (path.Length == 0) { result = TryDelete(ref branchNode.Children[BranchNode.ChildCount - 1], path); } else { - result = TryDelete(ref branchNode.Children[path[0]], path.Skip(1)); + result = TryDelete(ref branchNode.Children[path[0]], path[1..]); } if (!result) return false; var childrenIndexes = Array.Empty(); diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index 29f862361f..f56275c772 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -273,23 +273,6 @@ public void TestAdd() Assert.AreEqual("0c", a.Add(b).ToHexString()); } - [TestMethod] - public void TestSkip() - { - var s = "abcd01".HexToBytes(); - s = s.Skip(2); - Assert.AreEqual("01", s.ToHexString()); - - s = new byte[] { 0x01 }; - s = s.Skip(1); - Assert.AreEqual(0, s.Length); - s = s.Skip(2); - Assert.AreEqual(0, s.Length); - - s = null; - Assert.AreEqual(0, s.Skip(1).Length); - } - [TestMethod] public void TestCommonPrefix() { From f37f43ceefdf85c55c8e9229126b95e88ba7d12e Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 10 Apr 2020 17:54:19 +0800 Subject: [PATCH 102/171] Remove Helper.Add() --- src/neo/Helper.cs | 8 -------- src/neo/Trie/MPT/MPTTrie.cs | 24 +++++++++++++----------- tests/neo.UnitTests/UT_Helper.cs | 17 ----------------- 3 files changed, 13 insertions(+), 36 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index d67ac1573a..e9cea5192e 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -326,14 +326,6 @@ public static byte[] CommonPrefix(this byte[] a, byte[] b) return a[..i]; } - public static byte[] Add(this byte[] a, byte b) - { - var len = a is null ? 0 : a.Length; - Array.Resize(ref a, len + 1); - a[len] = b; - return a; - } - public static byte[] ToNibbles(this byte[] path) { if (path is null) return Array.Empty(); diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 55d8266768..5a2b4ef79c 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,6 +1,7 @@ using Neo.IO.Json; using Neo.Persistence; using System; +using System.Collections.Generic; using static Neo.Helper; namespace Neo.Trie.MPT @@ -182,13 +183,13 @@ private bool TryDelete(ref MPTNode node, byte[] path) result = TryDelete(ref branchNode.Children[path[0]], path[1..]); } if (!result) return false; - var childrenIndexes = Array.Empty(); + List childrenIndexes = new List(); for (int i = 0; i < BranchNode.ChildCount; i++) { if (branchNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; - childrenIndexes = childrenIndexes.Add((byte)i); + childrenIndexes.Add((byte)i); } - if (childrenIndexes.Length > 1) + if (childrenIndexes.Count > 1) { branchNode.SetDirty(); db.Put(branchNode); @@ -208,19 +209,20 @@ private bool TryDelete(ref MPTNode node, byte[] path) } if (lastChild is ExtensionNode exNode) { - exNode.Key = Concat(childrenIndexes, exNode.Key); + exNode.Key = Concat(childrenIndexes.ToArray(), exNode.Key); exNode.SetDirty(); db.Put(exNode); node = exNode; - return true; } - var newNode = new ExtensionNode() + else { - Key = childrenIndexes, - Next = lastChild, - }; - node = newNode; - db.Put(node); + node = new ExtensionNode() + { + Key = childrenIndexes.ToArray(), + Next = lastChild, + }; + db.Put(node); + } return true; } case HashNode hashNode: diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index f56275c772..09cbda8e2f 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -256,23 +256,6 @@ private static void CheckArgumentNullException(double start, double end, Func(); } - [TestMethod] - public void TestAdd() - { - var a = "ab".HexToBytes(); - byte b = 0x0c; - a = a.Add(b); - Assert.AreEqual("ab0c", a.ToHexString()); - - a = new byte[0]; - a = a.Add(b); - Assert.AreEqual(1, a.Length); - Assert.AreEqual("0c", a.ToHexString()); - - a = null; - Assert.AreEqual("0c", a.Add(b).ToHexString()); - } - [TestMethod] public void TestCommonPrefix() { From 5fa00cf2ed5d3b057e43d543c6c20b343c43d375 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 10 Apr 2020 17:58:28 +0800 Subject: [PATCH 103/171] Move methods --- src/neo/Helper.cs | 27 ------------ src/neo/Trie/MPT/Helper.cs | 34 ++++++++++++++ tests/neo.UnitTests/Trie/MPT/UT_Helper.cs | 54 +++++++++++++++++++++++ tests/neo.UnitTests/UT_Helper.cs | 45 ------------------- 4 files changed, 88 insertions(+), 72 deletions(-) create mode 100644 src/neo/Trie/MPT/Helper.cs create mode 100644 tests/neo.UnitTests/Trie/MPT/UT_Helper.cs diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index e9cea5192e..be66ce2013 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -310,32 +310,5 @@ internal static IEnumerable WeightedFilter(this IList so yield return resultSelector(item, weight); } } - - public static byte[] CommonPrefix(this byte[] a, byte[] b) - { - if (a is null || b is null) return Array.Empty(); - var minLen = a.Length <= b.Length ? a.Length : b.Length; - int i = 0; - if (a.Length != 0 && b.Length != 0) - { - for (i = 0; i < minLen; i++) - { - if (a[i] != b[i]) break; - } - } - return a[..i]; - } - - public static byte[] ToNibbles(this byte[] path) - { - if (path is null) return Array.Empty(); - var result = new byte[path.Length * 2]; - for (int i = 0; i < path.Length; i++) - { - result[i * 2] = (byte)(path[i] >> 4); - result[i * 2 + 1] = (byte)(path[i] & 0x0F); - } - return result; - } } } diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Trie/MPT/Helper.cs new file mode 100644 index 0000000000..ce4627c917 --- /dev/null +++ b/src/neo/Trie/MPT/Helper.cs @@ -0,0 +1,34 @@ +using System; + +namespace Neo.Trie.MPT +{ + internal static class Helper + { + public static byte[] CommonPrefix(this byte[] a, byte[] b) + { + if (a is null || b is null) return Array.Empty(); + var minLen = a.Length <= b.Length ? a.Length : b.Length; + int i = 0; + if (a.Length != 0 && b.Length != 0) + { + for (i = 0; i < minLen; i++) + { + if (a[i] != b[i]) break; + } + } + return a[..i]; + } + + public static byte[] ToNibbles(this byte[] path) + { + if (path is null) return Array.Empty(); + var result = new byte[path.Length * 2]; + for (int i = 0; i < path.Length; i++) + { + result[i * 2] = (byte)(path[i] >> 4); + result[i * 2 + 1] = (byte)(path[i] & 0x0F); + } + return result; + } + } +} diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs new file mode 100644 index 0000000000..86b11e3f68 --- /dev/null +++ b/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs @@ -0,0 +1,54 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Trie.MPT; + +namespace Neo.UnitTests.Trie.MPT +{ + [TestClass] + public class UT_Helper + { + [TestMethod] + public void TestCommonPrefix() + { + var a = "1234abcd".HexToBytes(); + var b = "".HexToBytes(); + var prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + + b = "100000".HexToBytes(); + prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + + b = "1234".HexToBytes(); + prefix = a.CommonPrefix(b); + Assert.AreEqual("1234", prefix.ToHexString()); + + b = a; + prefix = a.CommonPrefix(b); + Assert.AreEqual("1234abcd", prefix.ToHexString()); + + a = new byte[0]; + b = new byte[0]; + prefix = a.CommonPrefix(b); + Assert.IsTrue(prefix.Length == 0); + + a = "1234abcd".HexToBytes(); + b = null; + Assert.AreEqual(0, a.CommonPrefix(b).Length); + + a = null; + b = null; + Assert.AreEqual(0, a.CommonPrefix(b).Length); + } + + [TestMethod] + public void TestToNibbles() + { + var a = "1234abcd".HexToBytes(); + var n = a.ToNibbles(); + Assert.AreEqual("010203040a0b0c0d", n.ToHexString()); + + a = null; + Assert.AreEqual(0, a.ToNibbles().Length); + } + } +} diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index 09cbda8e2f..daeaabc43b 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -255,51 +255,6 @@ private static void CheckArgumentNullException(double start, double end, Func p.Weight, p => p.Weight); action.Should().Throw(); } - - [TestMethod] - public void TestCommonPrefix() - { - var a = "1234abcd".HexToBytes(); - var b = "".HexToBytes(); - var prefix = a.CommonPrefix(b); - Assert.IsTrue(prefix.Length == 0); - - b = "100000".HexToBytes(); - prefix = a.CommonPrefix(b); - Assert.IsTrue(prefix.Length == 0); - - b = "1234".HexToBytes(); - prefix = a.CommonPrefix(b); - Assert.AreEqual("1234", prefix.ToHexString()); - - b = a; - prefix = a.CommonPrefix(b); - Assert.AreEqual("1234abcd", prefix.ToHexString()); - - a = new byte[0]; - b = new byte[0]; - prefix = a.CommonPrefix(b); - Assert.IsTrue(prefix.Length == 0); - - a = "1234abcd".HexToBytes(); - b = null; - Assert.AreEqual(0, a.CommonPrefix(b).Length); - - a = null; - b = null; - Assert.AreEqual(0, a.CommonPrefix(b).Length); - } - - [TestMethod] - public void TestToNibbles() - { - var a = "1234abcd".HexToBytes(); - var n = a.ToNibbles(); - Assert.AreEqual("010203040a0b0c0d", n.ToHexString()); - - a = null; - Assert.AreEqual(0, a.ToNibbles().Length); - } } class Foo From 1c7b492555d9a18a43bd12e4ff457d76678916d1 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 10 Apr 2020 18:13:09 +0800 Subject: [PATCH 104/171] fix --- src/neo/Trie/MPT/MPTTrie.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 5a2b4ef79c..496c45ccfb 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -123,6 +123,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } var new_node = Resolve(hashNode); if (new_node is null) return false; + node = new_node; return Put(ref node, path, val); } default: From c09ef1f1a3298767688d5110b567bb0c13a01581 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 10 Apr 2020 17:47:23 +0200 Subject: [PATCH 105/171] Remove try for dispose --- src/neo/Trie/MPT/MPTNode.cs | 81 ++++++++++++++----------------------- 1 file changed, 31 insertions(+), 50 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 312efea929..16bbc32ef7 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -36,23 +36,14 @@ public MPTNode() public byte[] Encode() { - MemoryStream ms = null; - try - { - ms = new MemoryStream(); - using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) - { - writer.Write((byte)nType); - EncodeSpecific(writer); - writer.Flush(); - return ms.ToArray(); - } - } - finally - { - if (ms != null) - ms.Dispose(); - } + using MemoryStream ms = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8, true); + + writer.Write((byte)nType); + EncodeSpecific(writer); + writer.Flush(); + + return ms.ToArray(); } public abstract void EncodeSpecific(BinaryWriter writer); @@ -61,42 +52,32 @@ public static MPTNode Decode(byte[] data) { if (data is null || data.Length == 0) return null; - MemoryStream ms = null; - try + + using BinaryReader reader = new BinaryReader(new MemoryStream(data, false), Encoding.UTF8, false); + + var nodeType = (NodeType)reader.ReadByte(); + switch (nodeType) { - ms = new MemoryStream(data, false); - using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) - { - var nodeType = (NodeType)reader.ReadByte(); - switch (nodeType) + case NodeType.BranchNode: { - case NodeType.BranchNode: - { - var n = new BranchNode(); - n.DecodeSpecific(reader); - return n; - } - case NodeType.ExtensionNode: - { - var n = new ExtensionNode(); - n.DecodeSpecific(reader); - return n; - } - case NodeType.LeafNode: - { - var n = new LeafNode(); - n.DecodeSpecific(reader); - return n; - } - default: - throw new System.InvalidOperationException(); + var n = new BranchNode(); + n.DecodeSpecific(reader); + return n; } - } - } - finally - { - if (ms != null) - ms.Dispose(); + case NodeType.ExtensionNode: + { + var n = new ExtensionNode(); + n.DecodeSpecific(reader); + return n; + } + case NodeType.LeafNode: + { + var n = new LeafNode(); + n.DecodeSpecific(reader); + return n; + } + default: + throw new System.InvalidOperationException(); } } From 3bc6ab75f4308acb92dc34aea65cf0616dabdb87 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 10 Apr 2020 17:50:06 +0200 Subject: [PATCH 106/171] Fix ReadBytes error --- src/neo/Trie/MPT/MPTNode/HashNode.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index c0b13677ad..c0b5dca570 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -49,8 +49,8 @@ public override void DecodeSpecific(BinaryReader reader) Hash = null; return; } - if (len != UInt256.Length) throw new System.InvalidOperationException("Invalid hash bytes"); - Hash = new UInt256(reader.ReadBytes((int)len)); + if (len != UInt256.Length) throw new InvalidOperationException("Invalid hash bytes"); + Hash = new UInt256(reader.ReadFixedBytes((int)len)); } public override JObject ToJson() From 1fc4053e45f01c3bd729b539101229e9fd63eec2 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 10 Apr 2020 17:54:26 +0200 Subject: [PATCH 107/171] Clean code --- src/neo/Trie/MPT/MPTNode.cs | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index 16bbc32ef7..a52883e202 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -55,30 +55,16 @@ public static MPTNode Decode(byte[] data) using BinaryReader reader = new BinaryReader(new MemoryStream(data, false), Encoding.UTF8, false); - var nodeType = (NodeType)reader.ReadByte(); - switch (nodeType) + var n = (NodeType)reader.ReadByte() switch { - case NodeType.BranchNode: - { - var n = new BranchNode(); - n.DecodeSpecific(reader); - return n; - } - case NodeType.ExtensionNode: - { - var n = new ExtensionNode(); - n.DecodeSpecific(reader); - return n; - } - case NodeType.LeafNode: - { - var n = new LeafNode(); - n.DecodeSpecific(reader); - return n; - } - default: - throw new System.InvalidOperationException(); - } + NodeType.BranchNode => (MPTNode)new BranchNode(), + NodeType.ExtensionNode => new ExtensionNode(), + NodeType.LeafNode => new LeafNode(), + _ => throw new System.InvalidOperationException(), + }; + + n.DecodeSpecific(reader); + return n; } public abstract void DecodeSpecific(BinaryReader reader); From 20d27a0ccb1e94e960cad9dc345ae3589dba08d4 Mon Sep 17 00:00:00 2001 From: zhangtao Date: Sat, 11 Apr 2020 16:52:29 +0800 Subject: [PATCH 108/171] add max value lenght --- src/neo/Trie/MPT/MPTNode/ExtensionNode.cs | 1 + src/neo/Trie/MPT/MPTNode/LeafNode.cs | 4 ++++ src/neo/Trie/MPT/MPTTrie.cs | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs index 33e2df8b75..6983860bdd 100644 --- a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs @@ -7,6 +7,7 @@ namespace Neo.Trie.MPT { public class ExtensionNode : MPTNode { + //max lenght when store StorageKey public const int MaxKeyLength = (InteropService.Storage.MaxKeySize + sizeof(int) + InteropService.Storage.MaxKeySize / IO.Helper.GroupingSizeInBytes) * 2; public byte[] Key; public MPTNode Next; diff --git a/src/neo/Trie/MPT/MPTNode/LeafNode.cs b/src/neo/Trie/MPT/MPTNode/LeafNode.cs index 1db2f28954..d49c675269 100644 --- a/src/neo/Trie/MPT/MPTNode/LeafNode.cs +++ b/src/neo/Trie/MPT/MPTNode/LeafNode.cs @@ -1,11 +1,15 @@ using Neo.IO; using Neo.IO.Json; +using Neo.SmartContract; using System.IO; namespace Neo.Trie.MPT { public class LeafNode : MPTNode { + //the max size when store StorageItem + public const int MaxValueLength = 3 + InteropService.Storage.MaxValueSize + sizeof(bool); + public byte[] Value; public LeafNode() diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 496c45ccfb..d933744288 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -20,6 +20,8 @@ public bool Put(byte[] key, byte[] value) var path = key.ToNibbles(); if (ExtensionNode.MaxKeyLength < path.Length) return false; + if (LeafNode.MaxValueLength < value.Length) + return false; if (value.Length == 0) return TryDelete(ref root, path); var n = new LeafNode(value); From 920adc601ae224695face36d4641c1b3cf3d3b46 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 14 Apr 2020 14:21:39 +0800 Subject: [PATCH 109/171] add read max lenght --- src/neo/Trie/MPT/MPTNode/ExtensionNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/LeafNode.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs index 6983860bdd..5fef6ba8b8 100644 --- a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs @@ -26,7 +26,7 @@ public override void EncodeSpecific(BinaryWriter writer) public override void DecodeSpecific(BinaryReader reader) { - Key = reader.ReadVarBytes(); + Key = reader.ReadVarBytes(MaxKeyLength); var hashNode = new HashNode(); hashNode.DecodeSpecific(reader); Next = hashNode; diff --git a/src/neo/Trie/MPT/MPTNode/LeafNode.cs b/src/neo/Trie/MPT/MPTNode/LeafNode.cs index d49c675269..8620d5f5cf 100644 --- a/src/neo/Trie/MPT/MPTNode/LeafNode.cs +++ b/src/neo/Trie/MPT/MPTNode/LeafNode.cs @@ -29,7 +29,7 @@ public override void EncodeSpecific(BinaryWriter writer) public override void DecodeSpecific(BinaryReader reader) { - Value = reader.ReadVarBytes(); + Value = reader.ReadVarBytes(MaxValueLength); } public override JObject ToJson() From 73f4d65de642d67888913065b2f269938c813e8f Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 15 Apr 2020 17:07:15 +0800 Subject: [PATCH 110/171] branchnode value index --- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index 0e72df90b4..45149274ce 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -62,7 +62,7 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) { if (path.Length == 0) { - return TryGet(ref branchNode.Children[16], path, out value); + return TryGet(ref branchNode.Children[BranchNode.ChildCount - 1], path, out value); } return TryGet(ref branchNode.Children[path[0]], path[1..], out value); } @@ -117,7 +117,7 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) set.Add(branchNode.Encode()); if (path.Length == 0) { - return GetProof(ref branchNode.Children[16], path, set); + return GetProof(ref branchNode.Children[BranchNode.ChildCount - 1], path, set); } return GetProof(ref branchNode.Children[path[0]], path[1..], set); } From 394a87c05474ba13629660df4354fad9d4c99c8f Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 24 Apr 2020 14:13:27 +0800 Subject: [PATCH 111/171] code --- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 6 +++--- src/neo/Trie/MPT/MPTTrie.cs | 15 ++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index 45149274ce..002fe7e0d0 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -107,9 +107,9 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) case HashNode hashNode: { if (hashNode.IsEmptyNode) break; - var new_node = Resolve(hashNode); - if (new_node is null) break; - node = new_node; + var newNode = Resolve(hashNode); + if (newNode is null) break; + node = newNode; return GetProof(ref node, path, set); } case BranchNode branchNode: diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index d933744288..39f020b926 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -111,9 +111,10 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } case HashNode hashNode: { + MPTNode newNode; if (hashNode.IsEmptyNode) { - var newNode = new ExtensionNode() + newNode = new ExtensionNode() { Key = path, Next = val, @@ -123,9 +124,9 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) db.Put(node); return true; } - var new_node = Resolve(hashNode); - if (new_node is null) return false; - node = new_node; + newNode = Resolve(hashNode); + if (newNode is null) return false; + node = newNode; return Put(ref node, path, val); } default: @@ -234,9 +235,9 @@ private bool TryDelete(ref MPTNode node, byte[] path) { return true; } - var new_node = Resolve(hashNode); - if (new_node is null) return false; - node = new_node; + var newNode = Resolve(hashNode); + if (newNode is null) return false; + node = newNode; return TryDelete(ref node, path); } default: From 5ddf390313727349f81608a489d05c5786c9a678 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 24 Apr 2020 15:06:16 +0800 Subject: [PATCH 112/171] fix add longer key --- src/neo/Trie/MPT/MPTNode/HashNode.cs | 5 ++++- src/neo/Trie/MPT/MPTTrie.cs | 17 +++++++++++++---- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 13 +++++++++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index c0b5dca570..c87b7ee1ac 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -56,7 +56,10 @@ public override void DecodeSpecific(BinaryReader reader) public override JObject ToJson() { var json = new JObject(); - json["hash"] = Hash.ToString(); + if (!this.IsEmptyNode) + { + json["hash"] = Hash.ToString(); + } return json; } } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 39f020b926..ed9d59132e 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -34,10 +34,19 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { case LeafNode leafNode: { - if (path.Length == 0 && val is LeafNode v) + if (val is LeafNode v) { - node = v; - db.Put(node); + if (path.Length == 0) + { + node = v; + db.Put(node); + return true; + } + var branch = new BranchNode(); + branch.Children[BranchNode.ChildCount - 1] = leafNode; + Put(ref branch.Children[path[0]], path[1..], v); + db.Put(branch); + node = branch; return true; } return false; @@ -120,7 +129,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) Next = val, }; node = newNode; - if (!(val is HashNode)) db.Put(val); + if (val is LeafNode) db.Put(val); db.Put(node); return true; } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 81332a7583..2981dc34e3 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -242,5 +242,18 @@ public void TestVerifyProof() Assert.IsTrue(result); Assert.AreEqual(value.ToHexString(), "abcd"); } + + [TestMethod] + public void TestAddLongerKey() + { + var store = new MemoryStore(); + var snapshot = store.GetSnapshot(); + var mpt = new MPTTrie(null, snapshot, 0); + var result = mpt.Put(new byte[] { 0xab }, new byte[] { 0x01 }); + Assert.IsTrue(result); + result = mpt.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x02 }); + Assert.IsTrue(result); + Assert.AreEqual("", mpt.ToJson()); + } } } From fb6ddfd27f52db8b9014f9b6bfaeb8f02bdc3975 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 24 Apr 2020 15:12:14 +0800 Subject: [PATCH 113/171] fix ut --- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 2981dc34e3..6aba903a8d 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -253,7 +253,6 @@ public void TestAddLongerKey() Assert.IsTrue(result); result = mpt.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x02 }); Assert.IsTrue(result); - Assert.AreEqual("", mpt.ToJson()); } } } From 1140d4548f95b87cc92520ed7c6fe5d9e97e5d8c Mon Sep 17 00:00:00 2001 From: KickSeason Date: Fri, 24 Apr 2020 15:24:38 +0800 Subject: [PATCH 114/171] fix size --- src/neo/Trie/MPT/MPTNode/ExtensionNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs index 5fef6ba8b8..f1a46fea68 100644 --- a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs +++ b/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs @@ -8,7 +8,7 @@ namespace Neo.Trie.MPT public class ExtensionNode : MPTNode { //max lenght when store StorageKey - public const int MaxKeyLength = (InteropService.Storage.MaxKeySize + sizeof(int) + InteropService.Storage.MaxKeySize / IO.Helper.GroupingSizeInBytes) * 2; + public const int MaxKeyLength = (InteropService.Storage.MaxKeySize + sizeof(int)) * 2; public byte[] Key; public MPTNode Next; From 9a5ad98c97befaaf192cadfe210b903f35aec6cb Mon Sep 17 00:00:00 2001 From: KickSeason Date: Sun, 26 Apr 2020 13:54:50 +0800 Subject: [PATCH 115/171] avoid empth key extension node --- src/neo/Trie/MPT/MPTTrie.cs | 31 +++++++++++++--------- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 18 +++++++++++++ 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index ed9d59132e..f8707fb10d 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -123,14 +123,21 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) MPTNode newNode; if (hashNode.IsEmptyNode) { - newNode = new ExtensionNode() + if (path.Length == 0) { - Key = path, - Next = val, - }; + newNode = val; + } + else + { + newNode = new ExtensionNode() + { + Key = path, + Next = val, + }; + db.Put(newNode); + } node = newNode; if (val is LeafNode) db.Put(val); - db.Put(node); return true; } newNode = Resolve(hashNode); @@ -226,16 +233,14 @@ private bool TryDelete(ref MPTNode node, byte[] path) exNode.SetDirty(); db.Put(exNode); node = exNode; + return true; } - else + node = new ExtensionNode() { - node = new ExtensionNode() - { - Key = childrenIndexes.ToArray(), - Next = lastChild, - }; - db.Put(node); - } + Key = childrenIndexes.ToArray(), + Next = lastChild, + }; + db.Put(node); return true; } case HashNode hashNode: diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 6aba903a8d..d8b4a2f558 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -254,5 +254,23 @@ public void TestAddLongerKey() result = mpt.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x02 }); Assert.IsTrue(result); } + + [TestMethod] + public void TestSplitKey() + { + var store = new MemoryStore(); + var snapshot = store.GetSnapshot(); + var mpt1 = new MPTTrie(null, snapshot, 0); + Assert.IsTrue(mpt1.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); + Assert.IsTrue(mpt1.Put(new byte[] { 0xab }, new byte[] { 0x02 })); + Assert.IsTrue(mpt1.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set1)); + Assert.AreEqual(4, set1.Count); + var mpt2 = new MPTTrie(null, snapshot, 0); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x02 })); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); + Assert.IsTrue(mpt2.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set2)); + Assert.AreEqual(4, set2.Count); + Assert.AreEqual(mpt1.GetRoot(), mpt2.GetRoot()); + } } } From 174e44135f9c800974f730060ce7dbf9bd381f99 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Tue, 28 Apr 2020 16:12:55 +0800 Subject: [PATCH 116/171] use ISerializable as key value --- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 28 ++-- src/neo/Trie/MPT/MPTTrie.cs | 23 +-- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 160 +++++++++++++++------ 3 files changed, 148 insertions(+), 63 deletions(-) diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index 002fe7e0d0..b9c2807144 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -1,10 +1,13 @@ +using Neo.IO; using Neo.Persistence; using System; using System.Collections.Generic; namespace Neo.Trie.MPT { - public class MPTReadOnlyTrie + public class MPTReadOnlyTrie + where TKey : notnull, ISerializable + where TValue : class, ISerializable, new() { private MPTReadOnlyDb rodb; protected MPTNode root; @@ -26,15 +29,17 @@ public MPTReadOnlyTrie(UInt256 root, IReadOnlyStore store, byte prefix) } } - public MPTNode Resolve(HashNode hn) + public MPTNode Resolve(HashNode n) { - return rodb.Node(hn.Hash); + return rodb.Node(n.Hash); } - public bool TryGet(byte[] key, out byte[] value) + public TValue Get(TKey key) { - var path = key.ToNibbles(); - return TryGet(ref root, path, out value); + var path = key.ToArray().ToNibbles(); + if (path.Length == 0) return null; + var result = TryGet(ref root, path, out byte[] value); + return result ? value.AsSerializable() : null; } private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) @@ -84,10 +89,11 @@ public UInt256 GetRoot() return root.GetHash(); } - public bool GetProof(byte[] key, out HashSet set) + public bool GetProof(TKey key, out HashSet set) { set = new HashSet(ByteArrayEqualityComparer.Default); - var path = key.ToNibbles(); + var path = key.ToArray().ToNibbles(); + if (path.Length == 0) return false; return GetProof(ref root, path, set); } @@ -134,11 +140,11 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) return false; } - public static bool VerifyProof(UInt256 root, byte[] key, HashSet proof, out byte[] value) + public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) { var store = new MPTProofStore(proof); - var trie = new MPTReadOnlyTrie(root, store, 0); - return trie.TryGet(key, out value); + var trie = new MPTReadOnlyTrie(root, store, 0); + return trie.Get(key); } } } diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index f8707fb10d..8238a1a179 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,3 +1,4 @@ +using Neo.IO; using Neo.IO.Json; using Neo.Persistence; using System; @@ -6,7 +7,9 @@ namespace Neo.Trie.MPT { - public class MPTTrie : MPTReadOnlyTrie + public class MPTTrie : MPTReadOnlyTrie + where TKey : notnull, ISerializable + where TValue : class, ISerializable, new() { private MPTDb db; @@ -15,16 +18,17 @@ public MPTTrie(UInt256 root, ISnapshot store, byte prefix) : base(root, store, p this.db = new MPTDb(store, prefix); } - public bool Put(byte[] key, byte[] value) + public bool Put(TKey key, TValue value) { - var path = key.ToNibbles(); - if (ExtensionNode.MaxKeyLength < path.Length) + var path = key.ToArray().ToNibbles(); + var val = value.ToArray(); + if (ExtensionNode.MaxKeyLength < path.Length || path.Length == 0) return false; - if (LeafNode.MaxValueLength < value.Length) + if (LeafNode.MaxValueLength < val.Length) return false; - if (value.Length == 0) + if (val.Length == 0) return TryDelete(ref root, path); - var n = new LeafNode(value); + var n = new LeafNode(val); return Put(ref root, path, n); } @@ -150,9 +154,10 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } } - public bool TryDelete(byte[] key) + public bool TryDelete(TKey key) { - var path = key.ToNibbles(); + var path = key.ToArray().ToNibbles(); + if (path.Length == 0) return false; return TryDelete(ref root, path); } diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index d8b4a2f558..027f043611 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -1,13 +1,87 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Persistence; -using Neo.Trie; using Neo.Trie.MPT; using System; +using System.IO; using System.Collections.Generic; using System.Text; namespace Neo.UnitTests.Trie.MPT { + public class TestKey : ISerializable + { + private byte[] key; + + public int Size => key.Length; + + public TestKey() + { + + } + + public TestKey(byte[] key) + { + this.key = key; + } + public void Serialize(BinaryWriter writer) + { + writer.Write(key); + } + + public void Deserialize(BinaryReader reader) + { + key = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); + } + + public override string ToString() + { + return key.ToHexString(); + } + + public static implicit operator TestKey(byte[] key) + { + return new TestKey(key); + } + } + + public class TestValue : ISerializable + { + private byte[] value; + + public int Size => value.Length; + + public TestValue() + { + + } + + public TestValue(byte[] value) + { + this.value = value; + } + + public void Serialize(BinaryWriter writer) + { + writer.Write(value); + } + + public void Deserialize(BinaryReader reader) + { + value = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); + } + + public override string ToString() + { + return value.ToHexString(); + } + + public static implicit operator TestValue(byte[] value) + { + return new TestValue(value); + } + } + [TestClass] public class UT_MPTTrie { @@ -64,42 +138,42 @@ public void TestInit() [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); - var result = mpt.TryGet("ac01".HexToBytes(), out byte[] value); - Assert.IsTrue(result); - Assert.AreEqual("abcd", value.ToHexString()); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + TestValue value = mpt.Get("ac01".HexToBytes()); + Assert.IsNotNull(value); + Assert.AreEqual("abcd", value.ToString()); - result = mpt.TryGet("ac99".HexToBytes(), out value); - Assert.IsTrue(result); - Assert.AreEqual("2222", value.ToHexString()); + value = mpt.Get("ac99".HexToBytes()); + Assert.IsNotNull(value); + Assert.AreEqual("2222", value.ToString()); - result = mpt.TryGet("ab99".HexToBytes(), out value); - Assert.IsFalse(result); + value = mpt.Get("ab99".HexToBytes()); + Assert.IsNull(value); - result = mpt.TryGet("ac39".HexToBytes(), out value); - Assert.IsFalse(result); + value = mpt.Get("ac39".HexToBytes()); + Assert.IsNull(value); - result = mpt.TryGet("ac02".HexToBytes(), out value); - Assert.IsFalse(result); + value = mpt.Get("ac02".HexToBytes()); + Assert.IsNull(value); - result = mpt.TryGet("ac9910".HexToBytes(), out value); - Assert.AreEqual(false, result); + value = mpt.Get("ac9910".HexToBytes()); + Assert.IsNull(value); } [TestMethod] public void TestTryGetResolve() { - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); - var result = mpt.TryGet("acae".HexToBytes(), out byte[] value); - Assert.IsTrue(result); - Assert.IsTrue(MemoryExtensions.SequenceEqual(Encoding.ASCII.GetBytes("hello"), (value))); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + TestValue value = mpt.Get("acae".HexToBytes()); + Assert.IsNotNull(value); + Assert.AreEqual(Encoding.ASCII.GetBytes("hello").ToHexString(), value.ToString()); } [TestMethod] public void TestTryPut() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store.GetSnapshot(), 0); + var mpt = new MPTTrie(null, store.GetSnapshot(), 0); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); @@ -139,10 +213,10 @@ public void TestTryDelete() Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.GetHash().ToString()); Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.GetHash().ToString()); - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); var result = true; - result = mpt.TryGet("ac99".HexToBytes(), out byte[] value); - Assert.IsTrue(result); + TestValue value = mpt.Get("ac99".HexToBytes()); + Assert.IsNotNull(value); result = mpt.TryDelete("ac99".HexToBytes()); Assert.IsTrue(result); result = mpt.TryDelete("acae".HexToBytes()); @@ -155,30 +229,30 @@ public void TestDeleteSameValue() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt = new MPTTrie(null, snapshot, 0); + var mpt = new MPTTrie(null, snapshot, 0); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); - result = mpt.TryGet("ac01".HexToBytes(), out byte[] value); - Assert.IsTrue(result); - result = mpt.TryGet("ac02".HexToBytes(), out value); - Assert.IsTrue(result); + TestValue value = mpt.Get("ac01".HexToBytes()); + Assert.IsNotNull(value); + value = mpt.Get("ac02".HexToBytes()); + Assert.IsNotNull(value); result = mpt.TryDelete("ac01".HexToBytes()); - result = mpt.TryGet("ac02".HexToBytes(), out value); - Assert.IsTrue(result); + value = mpt.Get("ac02".HexToBytes()); + Assert.IsNotNull(value); snapshot.Commit(); - var mpt0 = new MPTTrie(mpt.GetRoot(), store.GetSnapshot(), 0); - result = mpt0.TryGet("ac02".HexToBytes(), out value); - Assert.IsTrue(result); + var mpt0 = new MPTTrie(mpt.GetRoot(), store.GetSnapshot(), 0); + value = mpt0.Get("ac02".HexToBytes()); + Assert.IsNotNull(value); } [TestMethod] public void TestBranchNodeRemainValue() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store.GetSnapshot(), 0); + var mpt = new MPTTrie(null, store.GetSnapshot(), 0); var result = mpt.Put("ac11".HexToBytes(), "ac11".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac22".HexToBytes(), "ac22".HexToBytes()); @@ -221,7 +295,7 @@ public void TestGetProof() l2.Next = v2; b.Children[10] = l3; - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); Assert.AreEqual(r.GetHash().ToString(), mpt.GetRoot().ToString()); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); @@ -235,12 +309,12 @@ public void TestGetProof() [TestMethod] public void TestVerifyProof() { - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); - result = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof, out byte[] value); - Assert.IsTrue(result); - Assert.AreEqual(value.ToHexString(), "abcd"); + TestValue value = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof); + Assert.IsNotNull(value); + Assert.AreEqual(value.ToString(), "abcd"); } [TestMethod] @@ -248,7 +322,7 @@ public void TestAddLongerKey() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt = new MPTTrie(null, snapshot, 0); + var mpt = new MPTTrie(null, snapshot, 0); var result = mpt.Put(new byte[] { 0xab }, new byte[] { 0x01 }); Assert.IsTrue(result); result = mpt.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x02 }); @@ -260,12 +334,12 @@ public void TestSplitKey() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt1 = new MPTTrie(null, snapshot, 0); + var mpt1 = new MPTTrie(null, snapshot, 0); Assert.IsTrue(mpt1.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); Assert.IsTrue(mpt1.Put(new byte[] { 0xab }, new byte[] { 0x02 })); Assert.IsTrue(mpt1.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set1)); Assert.AreEqual(4, set1.Count); - var mpt2 = new MPTTrie(null, snapshot, 0); + var mpt2 = new MPTTrie(null, snapshot, 0); Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x02 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); Assert.IsTrue(mpt2.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set2)); From 53613c216870273c547cc0301d1fd19e7890ad47 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 29 Apr 2020 11:32:27 +0800 Subject: [PATCH 117/171] add find and split class --- src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs | 110 +++++++++ src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs | 62 +++++ src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs | 70 ++++++ src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 112 +-------- src/neo/Trie/MPT/MPTTrie.Delete.cs | 124 ++++++++++ src/neo/Trie/MPT/MPTTrie.Put.cs | 150 +++++++++++++ src/neo/Trie/MPT/MPTTrie.cs | 250 +-------------------- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 35 ++- 8 files changed, 549 insertions(+), 364 deletions(-) create mode 100644 src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs create mode 100644 src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs create mode 100644 src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs create mode 100644 src/neo/Trie/MPT/MPTTrie.Delete.cs create mode 100644 src/neo/Trie/MPT/MPTTrie.Put.cs diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs new file mode 100644 index 0000000000..f89b341110 --- /dev/null +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs @@ -0,0 +1,110 @@ +using Neo.IO; +using System; +using System.Collections.Generic; +using static Neo.Helper; + +namespace Neo.Trie.MPT +{ + public partial class MPTReadOnlyTrie + where TKey : notnull, ISerializable, new() + where TValue : class, ISerializable, new() + { + private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) + { + switch (node) + { + case LeafNode leafNode: + { + if (path.Length == 0) + { + start = leafNode; + return Array.Empty(); + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) break; + var newNode = Resolve(hashNode); + if (newNode is null) break; + node = newNode; + return Seek(ref node, path, out start); + } + case BranchNode branchNode: + { + if (path.Length == 0) + { + start = branchNode; + return Array.Empty(); + } + return Concat(path[..1], Seek(ref branchNode.Children[path[0]], path[1..], out start)); + } + case ExtensionNode extensionNode: + { + if (path.Length == 0) + { + start = extensionNode; + return Array.Empty(); + } + if (path.AsSpan().StartsWith(extensionNode.Key)) + { + return Concat(extensionNode.Key, Seek(ref extensionNode.Next, path[extensionNode.Key.Length..], out start)); + } + if (extensionNode.Key.AsSpan().StartsWith(path)) + { + start = extensionNode; + return extensionNode.Key[path.Length..]; + } + break; + } + } + start = null; + return Array.Empty(); + } + + public IEnumerable<(TKey Key, TValue Value)> Find(byte[] prefix) + { + var path = prefix.ToNibbles(); + path = Seek(ref root, path, out MPTNode start); + foreach (var item in Travers(start, path)) + yield return (item.Key.AsSerializable(), item.Value.AsSerializable()); + } + + private IEnumerable<(byte[] Key, byte[] Value)> Travers(MPTNode node, byte[] path) + { + switch (node) + { + case LeafNode leafNode: + { + yield return (path, (byte[])leafNode.Value.Clone()); + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) break; + var newNode = Resolve(hashNode); + if (newNode is null) break; + node = newNode; + foreach (var item in Travers(node, path)) + yield return item; + break; + } + case BranchNode branchNode: + { + for (int i = 0; i < BranchNode.ChildCount; i++) + { + foreach (var item in Travers(branchNode.Children[i], Concat(path, new byte[] { (byte)i }))) + yield return item; + } + break; + } + case ExtensionNode extensionNode: + { + foreach (var item in Travers(extensionNode.Next, Concat(path, extensionNode.Key))) + yield return item; + break; + } + } + } + } +} \ No newline at end of file diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs new file mode 100644 index 0000000000..8a7b4814cc --- /dev/null +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs @@ -0,0 +1,62 @@ +using Neo.IO; +using Neo.Persistence; +using System; +using System.Collections.Generic; + +namespace Neo.Trie.MPT +{ + public partial class MPTReadOnlyTrie + where TKey : notnull, ISerializable, new() + where TValue : class, ISerializable, new() + { + public TValue Get(TKey key) + { + var path = key.ToArray().ToNibbles(); + if (path.Length == 0) return null; + var result = TryGet(ref root, path, out byte[] value); + return result ? value.AsSerializable() : null; + } + + private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) + { + switch (node) + { + case LeafNode leafNode: + { + if (path.Length == 0) + { + value = (byte[])leafNode.Value.Clone(); + return true; + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) break; + var newNode = Resolve(hashNode); + if (newNode is null) break; + node = newNode; + return TryGet(ref node, path, out value); + } + case BranchNode branchNode: + { + if (path.Length == 0) + { + return TryGet(ref branchNode.Children[BranchNode.ChildCount - 1], path, out value); + } + return TryGet(ref branchNode.Children[path[0]], path[1..], out value); + } + case ExtensionNode extensionNode: + { + if (path.AsSpan().StartsWith(extensionNode.Key)) + { + return TryGet(ref extensionNode.Next, path[extensionNode.Key.Length..], out value); + } + break; + } + } + value = Array.Empty(); + return false; + } + } +} diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs new file mode 100644 index 0000000000..a4f7c5b826 --- /dev/null +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs @@ -0,0 +1,70 @@ +using Neo.IO; +using Neo.Persistence; +using System; +using System.Collections.Generic; + +namespace Neo.Trie.MPT +{ + public partial class MPTReadOnlyTrie + where TKey : notnull, ISerializable, new() + where TValue : class, ISerializable, new() + { + public bool GetProof(TKey key, out HashSet set) + { + set = new HashSet(ByteArrayEqualityComparer.Default); + var path = key.ToArray().ToNibbles(); + if (path.Length == 0) return false; + return GetProof(ref root, path, set); + } + + private bool GetProof(ref MPTNode node, byte[] path, HashSet set) + { + switch (node) + { + case LeafNode leafNode: + { + if (path.Length == 0) + { + set.Add(leafNode.Encode()); + return true; + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) break; + var newNode = Resolve(hashNode); + if (newNode is null) break; + node = newNode; + return GetProof(ref node, path, set); + } + case BranchNode branchNode: + { + set.Add(branchNode.Encode()); + if (path.Length == 0) + { + return GetProof(ref branchNode.Children[BranchNode.ChildCount - 1], path, set); + } + return GetProof(ref branchNode.Children[path[0]], path[1..], set); + } + case ExtensionNode extensionNode: + { + if (path.AsSpan().StartsWith(extensionNode.Key)) + { + set.Add(extensionNode.Encode()); + return GetProof(ref extensionNode.Next, path[extensionNode.Key.Length..], set); + } + break; + } + } + return false; + } + + public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) + { + var store = new MPTProofStore(proof); + var trie = new MPTReadOnlyTrie(root, store, 0); + return trie.Get(key); + } + } +} diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index b9c2807144..69ac100a5d 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -5,8 +5,8 @@ namespace Neo.Trie.MPT { - public class MPTReadOnlyTrie - where TKey : notnull, ISerializable + public partial class MPTReadOnlyTrie + where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { private MPTReadOnlyDb rodb; @@ -34,117 +34,9 @@ public MPTNode Resolve(HashNode n) return rodb.Node(n.Hash); } - public TValue Get(TKey key) - { - var path = key.ToArray().ToNibbles(); - if (path.Length == 0) return null; - var result = TryGet(ref root, path, out byte[] value); - return result ? value.AsSerializable() : null; - } - - private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) - { - switch (node) - { - case LeafNode leafNode: - { - if (path.Length == 0) - { - value = (byte[])leafNode.Value.Clone(); - return true; - } - break; - } - case HashNode hashNode: - { - if (hashNode.IsEmptyNode) break; - var newNode = Resolve(hashNode); - if (newNode is null) break; - node = newNode; - return TryGet(ref node, path, out value); - } - case BranchNode branchNode: - { - if (path.Length == 0) - { - return TryGet(ref branchNode.Children[BranchNode.ChildCount - 1], path, out value); - } - return TryGet(ref branchNode.Children[path[0]], path[1..], out value); - } - case ExtensionNode extensionNode: - { - if (path.AsSpan().StartsWith(extensionNode.Key)) - { - return TryGet(ref extensionNode.Next, path[extensionNode.Key.Length..], out value); - } - break; - } - } - value = Array.Empty(); - return false; - } - public UInt256 GetRoot() { return root.GetHash(); } - - public bool GetProof(TKey key, out HashSet set) - { - set = new HashSet(ByteArrayEqualityComparer.Default); - var path = key.ToArray().ToNibbles(); - if (path.Length == 0) return false; - return GetProof(ref root, path, set); - } - - private bool GetProof(ref MPTNode node, byte[] path, HashSet set) - { - switch (node) - { - case LeafNode leafNode: - { - if (path.Length == 0) - { - set.Add(leafNode.Encode()); - return true; - } - break; - } - case HashNode hashNode: - { - if (hashNode.IsEmptyNode) break; - var newNode = Resolve(hashNode); - if (newNode is null) break; - node = newNode; - return GetProof(ref node, path, set); - } - case BranchNode branchNode: - { - set.Add(branchNode.Encode()); - if (path.Length == 0) - { - return GetProof(ref branchNode.Children[BranchNode.ChildCount - 1], path, set); - } - return GetProof(ref branchNode.Children[path[0]], path[1..], set); - } - case ExtensionNode extensionNode: - { - if (path.AsSpan().StartsWith(extensionNode.Key)) - { - set.Add(extensionNode.Encode()); - return GetProof(ref extensionNode.Next, path[extensionNode.Key.Length..], set); - } - break; - } - } - return false; - } - - public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) - { - var store = new MPTProofStore(proof); - var trie = new MPTReadOnlyTrie(root, store, 0); - return trie.Get(key); - } } } diff --git a/src/neo/Trie/MPT/MPTTrie.Delete.cs b/src/neo/Trie/MPT/MPTTrie.Delete.cs new file mode 100644 index 0000000000..2a76a42bbb --- /dev/null +++ b/src/neo/Trie/MPT/MPTTrie.Delete.cs @@ -0,0 +1,124 @@ +using Neo.IO; +using Neo.IO.Json; +using Neo.Persistence; +using System; +using System.Collections.Generic; +using static Neo.Helper; + +namespace Neo.Trie.MPT +{ + public partial class MPTTrie : MPTReadOnlyTrie + where TKey : notnull, ISerializable, new() + where TValue : class, ISerializable, new() + { + public bool Delete(TKey key) + { + var path = key.ToArray().ToNibbles(); + if (path.Length == 0) return false; + return TryDelete(ref root, path); + } + + private bool TryDelete(ref MPTNode node, byte[] path) + { + switch (node) + { + case LeafNode leafNode: + { + if (path.Length == 0) + { + node = HashNode.EmptyNode(); + return true; + } + return false; + } + case ExtensionNode extensionNode: + { + if (path.AsSpan().StartsWith(extensionNode.Key)) + { + var result = TryDelete(ref extensionNode.Next, path[extensionNode.Key.Length..]); + if (!result) return false; + if (extensionNode.Next is HashNode hashNode && hashNode.IsEmptyNode) + { + node = extensionNode.Next; + return true; + } + if (extensionNode.Next is ExtensionNode sn) + { + extensionNode.Key = Concat(extensionNode.Key, sn.Key); + extensionNode.Next = sn.Next; + } + extensionNode.SetDirty(); + db.Put(extensionNode); + return true; + } + return false; + } + case BranchNode branchNode: + { + bool result; + if (path.Length == 0) + { + result = TryDelete(ref branchNode.Children[BranchNode.ChildCount - 1], path); + } + else + { + result = TryDelete(ref branchNode.Children[path[0]], path[1..]); + } + if (!result) return false; + List childrenIndexes = new List(); + for (int i = 0; i < BranchNode.ChildCount; i++) + { + if (branchNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; + childrenIndexes.Add((byte)i); + } + if (childrenIndexes.Count > 1) + { + branchNode.SetDirty(); + db.Put(branchNode); + return true; + } + var lastChildIndex = childrenIndexes[0]; + var lastChild = branchNode.Children[lastChildIndex]; + if (lastChildIndex == BranchNode.ChildCount - 1) + { + node = lastChild; + return true; + } + if (lastChild is HashNode hashNode) + { + lastChild = Resolve(hashNode); + if (lastChild is null) return false; + } + if (lastChild is ExtensionNode exNode) + { + exNode.Key = Concat(childrenIndexes.ToArray(), exNode.Key); + exNode.SetDirty(); + db.Put(exNode); + node = exNode; + return true; + } + node = new ExtensionNode() + { + Key = childrenIndexes.ToArray(), + Next = lastChild, + }; + db.Put(node); + return true; + } + case HashNode hashNode: + { + if (hashNode.IsEmptyNode) + { + return true; + } + var newNode = Resolve(hashNode); + if (newNode is null) return false; + node = newNode; + return TryDelete(ref node, path); + } + default: + return false; + } + } + } +} diff --git a/src/neo/Trie/MPT/MPTTrie.Put.cs b/src/neo/Trie/MPT/MPTTrie.Put.cs new file mode 100644 index 0000000000..8583a5afab --- /dev/null +++ b/src/neo/Trie/MPT/MPTTrie.Put.cs @@ -0,0 +1,150 @@ +using Neo.IO; +using Neo.IO.Json; +using Neo.Persistence; +using System; +using System.Collections.Generic; +using static Neo.Helper; + +namespace Neo.Trie.MPT +{ + public partial class MPTTrie : MPTReadOnlyTrie + where TKey : notnull, ISerializable, new() + where TValue : class, ISerializable, new() + { + public bool Put(TKey key, TValue value) + { + var path = key.ToArray().ToNibbles(); + var val = value.ToArray(); + if (ExtensionNode.MaxKeyLength < path.Length || path.Length == 0) + return false; + if (LeafNode.MaxValueLength < val.Length) + return false; + if (val.Length == 0) + return TryDelete(ref root, path); + var n = new LeafNode(val); + return Put(ref root, path, n); + } + + private bool Put(ref MPTNode node, byte[] path, MPTNode val) + { + switch (node) + { + case LeafNode leafNode: + { + if (val is LeafNode v) + { + if (path.Length == 0) + { + node = v; + db.Put(node); + return true; + } + var branch = new BranchNode(); + branch.Children[BranchNode.ChildCount - 1] = leafNode; + Put(ref branch.Children[path[0]], path[1..], v); + db.Put(branch); + node = branch; + return true; + } + return false; + } + case ExtensionNode extensionNode: + { + if (path.AsSpan().StartsWith(extensionNode.Key)) + { + var result = Put(ref extensionNode.Next, path[extensionNode.Key.Length..], val); + if (result) + { + extensionNode.SetDirty(); + db.Put(extensionNode); + } + return result; + } + var prefix = extensionNode.Key.CommonPrefix(path); + var pathRemain = path[prefix.Length..]; + var keyRemain = extensionNode.Key[prefix.Length..]; + var son = new BranchNode(); + MPTNode grandSon1 = HashNode.EmptyNode(); + MPTNode grandSon2 = HashNode.EmptyNode(); + + Put(ref grandSon1, keyRemain[1..], extensionNode.Next); + son.Children[keyRemain[0]] = grandSon1; + + if (pathRemain.Length == 0) + { + Put(ref grandSon2, pathRemain, val); + son.Children[BranchNode.ChildCount - 1] = grandSon2; + } + else + { + Put(ref grandSon2, pathRemain[1..], val); + son.Children[pathRemain[0]] = grandSon2; + } + db.Put(son); + if (prefix.Length > 0) + { + var exNode = new ExtensionNode() + { + Key = prefix, + Next = son, + }; + db.Put(exNode); + node = exNode; + } + else + { + node = son; + } + return true; + } + case BranchNode branchNode: + { + bool result; + if (path.Length == 0) + { + result = Put(ref branchNode.Children[BranchNode.ChildCount - 1], path, val); + } + else + { + result = Put(ref branchNode.Children[path[0]], path[1..], val); + } + if (result) + { + branchNode.SetDirty(); + db.Put(branchNode); + } + return result; + } + case HashNode hashNode: + { + MPTNode newNode; + if (hashNode.IsEmptyNode) + { + if (path.Length == 0) + { + newNode = val; + } + else + { + newNode = new ExtensionNode() + { + Key = path, + Next = val, + }; + db.Put(newNode); + } + node = newNode; + if (val is LeafNode) db.Put(val); + return true; + } + newNode = Resolve(hashNode); + if (newNode is null) return false; + node = newNode; + return Put(ref node, path, val); + } + default: + return false; + } + } + } +} diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index 8238a1a179..ed66d72b3b 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -7,8 +7,8 @@ namespace Neo.Trie.MPT { - public class MPTTrie : MPTReadOnlyTrie - where TKey : notnull, ISerializable + public partial class MPTTrie : MPTReadOnlyTrie + where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { private MPTDb db; @@ -18,252 +18,6 @@ public MPTTrie(UInt256 root, ISnapshot store, byte prefix) : base(root, store, p this.db = new MPTDb(store, prefix); } - public bool Put(TKey key, TValue value) - { - var path = key.ToArray().ToNibbles(); - var val = value.ToArray(); - if (ExtensionNode.MaxKeyLength < path.Length || path.Length == 0) - return false; - if (LeafNode.MaxValueLength < val.Length) - return false; - if (val.Length == 0) - return TryDelete(ref root, path); - var n = new LeafNode(val); - return Put(ref root, path, n); - } - - private bool Put(ref MPTNode node, byte[] path, MPTNode val) - { - switch (node) - { - case LeafNode leafNode: - { - if (val is LeafNode v) - { - if (path.Length == 0) - { - node = v; - db.Put(node); - return true; - } - var branch = new BranchNode(); - branch.Children[BranchNode.ChildCount - 1] = leafNode; - Put(ref branch.Children[path[0]], path[1..], v); - db.Put(branch); - node = branch; - return true; - } - return false; - } - case ExtensionNode extensionNode: - { - if (path.AsSpan().StartsWith(extensionNode.Key)) - { - var result = Put(ref extensionNode.Next, path[extensionNode.Key.Length..], val); - if (result) - { - extensionNode.SetDirty(); - db.Put(extensionNode); - } - return result; - } - var prefix = extensionNode.Key.CommonPrefix(path); - var pathRemain = path[prefix.Length..]; - var keyRemain = extensionNode.Key[prefix.Length..]; - var son = new BranchNode(); - MPTNode grandSon1 = HashNode.EmptyNode(); - MPTNode grandSon2 = HashNode.EmptyNode(); - - Put(ref grandSon1, keyRemain[1..], extensionNode.Next); - son.Children[keyRemain[0]] = grandSon1; - - if (pathRemain.Length == 0) - { - Put(ref grandSon2, pathRemain, val); - son.Children[BranchNode.ChildCount - 1] = grandSon2; - } - else - { - Put(ref grandSon2, pathRemain[1..], val); - son.Children[pathRemain[0]] = grandSon2; - } - db.Put(son); - if (prefix.Length > 0) - { - var exNode = new ExtensionNode() - { - Key = prefix, - Next = son, - }; - db.Put(exNode); - node = exNode; - } - else - { - node = son; - } - return true; - } - case BranchNode branchNode: - { - bool result; - if (path.Length == 0) - { - result = Put(ref branchNode.Children[BranchNode.ChildCount - 1], path, val); - } - else - { - result = Put(ref branchNode.Children[path[0]], path[1..], val); - } - if (result) - { - branchNode.SetDirty(); - db.Put(branchNode); - } - return result; - } - case HashNode hashNode: - { - MPTNode newNode; - if (hashNode.IsEmptyNode) - { - if (path.Length == 0) - { - newNode = val; - } - else - { - newNode = new ExtensionNode() - { - Key = path, - Next = val, - }; - db.Put(newNode); - } - node = newNode; - if (val is LeafNode) db.Put(val); - return true; - } - newNode = Resolve(hashNode); - if (newNode is null) return false; - node = newNode; - return Put(ref node, path, val); - } - default: - return false; - } - } - - public bool TryDelete(TKey key) - { - var path = key.ToArray().ToNibbles(); - if (path.Length == 0) return false; - return TryDelete(ref root, path); - } - - private bool TryDelete(ref MPTNode node, byte[] path) - { - switch (node) - { - case LeafNode leafNode: - { - if (path.Length == 0) - { - node = HashNode.EmptyNode(); - return true; - } - return false; - } - case ExtensionNode extensionNode: - { - if (path.AsSpan().StartsWith(extensionNode.Key)) - { - var result = TryDelete(ref extensionNode.Next, path[extensionNode.Key.Length..]); - if (!result) return false; - if (extensionNode.Next is HashNode hashNode && hashNode.IsEmptyNode) - { - node = extensionNode.Next; - return true; - } - if (extensionNode.Next is ExtensionNode sn) - { - extensionNode.Key = Concat(extensionNode.Key, sn.Key); - extensionNode.Next = sn.Next; - } - extensionNode.SetDirty(); - db.Put(extensionNode); - return true; - } - return false; - } - case BranchNode branchNode: - { - bool result; - if (path.Length == 0) - { - result = TryDelete(ref branchNode.Children[BranchNode.ChildCount - 1], path); - } - else - { - result = TryDelete(ref branchNode.Children[path[0]], path[1..]); - } - if (!result) return false; - List childrenIndexes = new List(); - for (int i = 0; i < BranchNode.ChildCount; i++) - { - if (branchNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; - childrenIndexes.Add((byte)i); - } - if (childrenIndexes.Count > 1) - { - branchNode.SetDirty(); - db.Put(branchNode); - return true; - } - var lastChildIndex = childrenIndexes[0]; - var lastChild = branchNode.Children[lastChildIndex]; - if (lastChildIndex == BranchNode.ChildCount - 1) - { - node = lastChild; - return true; - } - if (lastChild is HashNode hashNode) - { - lastChild = Resolve(hashNode); - if (lastChild is null) return false; - } - if (lastChild is ExtensionNode exNode) - { - exNode.Key = Concat(childrenIndexes.ToArray(), exNode.Key); - exNode.SetDirty(); - db.Put(exNode); - node = exNode; - return true; - } - node = new ExtensionNode() - { - Key = childrenIndexes.ToArray(), - Next = lastChild, - }; - db.Put(node); - return true; - } - case HashNode hashNode: - { - if (hashNode.IsEmptyNode) - { - return true; - } - var newNode = Resolve(hashNode); - if (newNode is null) return false; - node = newNode; - return TryDelete(ref node, path); - } - default: - return false; - } - } - public JObject ToJson() { return root.ToJson(); diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 027f043611..53f56742e9 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -3,8 +3,9 @@ using Neo.Persistence; using Neo.Trie.MPT; using System; -using System.IO; using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Text; namespace Neo.UnitTests.Trie.MPT @@ -217,9 +218,9 @@ public void TestTryDelete() var result = true; TestValue value = mpt.Get("ac99".HexToBytes()); Assert.IsNotNull(value); - result = mpt.TryDelete("ac99".HexToBytes()); + result = mpt.Delete("ac99".HexToBytes()); Assert.IsTrue(result); - result = mpt.TryDelete("acae".HexToBytes()); + result = mpt.Delete("acae".HexToBytes()); Assert.IsTrue(result); Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", mpt.GetRoot().ToString()); } @@ -238,7 +239,7 @@ public void TestDeleteSameValue() Assert.IsNotNull(value); value = mpt.Get("ac02".HexToBytes()); Assert.IsNotNull(value); - result = mpt.TryDelete("ac01".HexToBytes()); + result = mpt.Delete("ac01".HexToBytes()); value = mpt.Get("ac02".HexToBytes()); Assert.IsNotNull(value); snapshot.Commit(); @@ -259,9 +260,9 @@ public void TestBranchNodeRemainValue() Assert.IsTrue(result); result = mpt.Put("ac".HexToBytes(), "ac".HexToBytes()); Assert.IsTrue(result); - result = mpt.TryDelete("ac11".HexToBytes()); + result = mpt.Delete("ac11".HexToBytes()); Assert.IsTrue(result); - result = mpt.TryDelete("ac22".HexToBytes()); + result = mpt.Delete("ac22".HexToBytes()); Assert.IsTrue(result); Assert.AreEqual("{\"key\":\"0a0c\",\"next\":{\"value\":\"ac\"}}", mpt.ToJson().ToString()); } @@ -346,5 +347,27 @@ public void TestSplitKey() Assert.AreEqual(4, set2.Count); Assert.AreEqual(mpt1.GetRoot(), mpt2.GetRoot()); } + + [TestMethod] + public void TestFind() + { + var store = new MemoryStore(); + var snapshot = store.GetSnapshot(); + var mpt1 = new MPTTrie(null, snapshot, 0); + var results = mpt1.Find(Array.Empty()).ToArray(); + Assert.AreEqual(0, results.Count()); + var mpt2 = new MPTTrie(null, snapshot, 0); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xef }, new byte[] { 0x01 })); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xe1 }, new byte[] { 0x02 })); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x03 })); + results = mpt2.Find(Array.Empty()).ToArray(); + Assert.AreEqual(3, results.Count()); + results = mpt2.Find(new byte[] { 0xab }).ToArray(); + Assert.AreEqual(3, results.Count()); + results = mpt2.Find(new byte[] { 0xab, 0xcd }).ToArray(); + Assert.AreEqual(2, results.Count()); + results = mpt2.Find(new byte[] { 0xac }).ToArray(); + Assert.AreEqual(0, results.Count()); + } } } From 6c1ab3b7f05c76dbf1777093843c7f946eca19de Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 29 Apr 2020 11:39:03 +0800 Subject: [PATCH 118/171] format --- src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs | 2 +- src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs | 2 -- src/neo/Trie/MPT/MPTReadOnlyTrie.cs | 2 -- src/neo/Trie/MPT/MPTTrie.Delete.cs | 2 -- src/neo/Trie/MPT/MPTTrie.Put.cs | 4 ---- src/neo/Trie/MPT/MPTTrie.cs | 3 --- 6 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs index f89b341110..11c32fcbc7 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs @@ -107,4 +107,4 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) } } } -} \ No newline at end of file +} diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs index 8a7b4814cc..9ffdf857b4 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs @@ -1,7 +1,5 @@ using Neo.IO; -using Neo.Persistence; using System; -using System.Collections.Generic; namespace Neo.Trie.MPT { diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs index 69ac100a5d..e16de54b05 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.cs @@ -1,7 +1,5 @@ using Neo.IO; using Neo.Persistence; -using System; -using System.Collections.Generic; namespace Neo.Trie.MPT { diff --git a/src/neo/Trie/MPT/MPTTrie.Delete.cs b/src/neo/Trie/MPT/MPTTrie.Delete.cs index 2a76a42bbb..6819b01dcd 100644 --- a/src/neo/Trie/MPT/MPTTrie.Delete.cs +++ b/src/neo/Trie/MPT/MPTTrie.Delete.cs @@ -1,6 +1,4 @@ using Neo.IO; -using Neo.IO.Json; -using Neo.Persistence; using System; using System.Collections.Generic; using static Neo.Helper; diff --git a/src/neo/Trie/MPT/MPTTrie.Put.cs b/src/neo/Trie/MPT/MPTTrie.Put.cs index 8583a5afab..8e120aee8b 100644 --- a/src/neo/Trie/MPT/MPTTrie.Put.cs +++ b/src/neo/Trie/MPT/MPTTrie.Put.cs @@ -1,9 +1,5 @@ using Neo.IO; -using Neo.IO.Json; -using Neo.Persistence; using System; -using System.Collections.Generic; -using static Neo.Helper; namespace Neo.Trie.MPT { diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Trie/MPT/MPTTrie.cs index ed66d72b3b..2b5f23ba6a 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Trie/MPT/MPTTrie.cs @@ -1,9 +1,6 @@ using Neo.IO; using Neo.IO.Json; using Neo.Persistence; -using System; -using System.Collections.Generic; -using static Neo.Helper; namespace Neo.Trie.MPT { From ae4c59058e1bb41a3925c5b2ff3a770f6269252c Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 29 Apr 2020 14:16:27 +0800 Subject: [PATCH 119/171] define mpt element accessor --- src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs index 9ffdf857b4..e96bd5665c 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs @@ -56,5 +56,13 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) value = Array.Empty(); return false; } + + public TValue this[TKey key] + { + get + { + return Get(key); + } + } } } From 14bc074c2470901f01b0ae4466b325883ed8d179 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 30 Apr 2020 15:10:34 +0800 Subject: [PATCH 120/171] assign default value of TestKey and TestValue in constructor --- tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs index 53f56742e9..bbb1781f36 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs @@ -18,7 +18,7 @@ public class TestKey : ISerializable public TestKey() { - + this.key = Array.Empty(); } public TestKey(byte[] key) @@ -54,7 +54,7 @@ public class TestValue : ISerializable public TestValue() { - + this.value = Array.Empty(); } public TestValue(byte[] value) From 3650852bf417706f1b4e3c3e1e5aca1903a5c0dc Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 30 Apr 2020 15:26:51 +0800 Subject: [PATCH 121/171] set value null when not found --- src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs index e96bd5665c..bfd9509c22 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs @@ -53,7 +53,7 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) break; } } - value = Array.Empty(); + value = null; return false; } From 47dd1c5361b02c9493020fa1ec056954c8b5f0b3 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 30 Apr 2020 15:33:52 +0800 Subject: [PATCH 122/171] use .Length < 1 --- src/neo/Trie/MPT/MPTNode.cs | 2 +- src/neo/Trie/MPT/MPTNode/HashNode.cs | 2 +- src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs | 6 +++--- src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs | 6 +++--- src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs | 6 +++--- src/neo/Trie/MPT/MPTTrie.Delete.cs | 6 +++--- src/neo/Trie/MPT/MPTTrie.Put.cs | 12 ++++++------ 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Trie/MPT/MPTNode.cs index a52883e202..52803bcc24 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Trie/MPT/MPTNode.cs @@ -50,7 +50,7 @@ public byte[] Encode() public static MPTNode Decode(byte[] data) { - if (data is null || data.Length == 0) + if (data is null || data.Length < 1) return null; using BinaryReader reader = new BinaryReader(new MemoryStream(data, false), Encoding.UTF8, false); diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Trie/MPT/MPTNode/HashNode.cs index c87b7ee1ac..9780bd0935 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Trie/MPT/MPTNode/HashNode.cs @@ -44,7 +44,7 @@ public override void EncodeSpecific(BinaryWriter writer) public override void DecodeSpecific(BinaryReader reader) { var len = reader.ReadVarInt(); - if (len == 0) + if (len < 1) { Hash = null; return; diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs index 11c32fcbc7..bc4b158cb0 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs @@ -15,7 +15,7 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) { case LeafNode leafNode: { - if (path.Length == 0) + if (path.Length < 1) { start = leafNode; return Array.Empty(); @@ -32,7 +32,7 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) } case BranchNode branchNode: { - if (path.Length == 0) + if (path.Length < 1) { start = branchNode; return Array.Empty(); @@ -41,7 +41,7 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) } case ExtensionNode extensionNode: { - if (path.Length == 0) + if (path.Length < 1) { start = extensionNode; return Array.Empty(); diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs index bfd9509c22..a92ea69bf6 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs @@ -10,7 +10,7 @@ public partial class MPTReadOnlyTrie public TValue Get(TKey key) { var path = key.ToArray().ToNibbles(); - if (path.Length == 0) return null; + if (path.Length < 1) return null; var result = TryGet(ref root, path, out byte[] value); return result ? value.AsSerializable() : null; } @@ -21,7 +21,7 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) { case LeafNode leafNode: { - if (path.Length == 0) + if (path.Length < 1) { value = (byte[])leafNode.Value.Clone(); return true; @@ -38,7 +38,7 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) } case BranchNode branchNode: { - if (path.Length == 0) + if (path.Length < 1) { return TryGet(ref branchNode.Children[BranchNode.ChildCount - 1], path, out value); } diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs b/src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs index a4f7c5b826..73c487d560 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs +++ b/src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs @@ -13,7 +13,7 @@ public bool GetProof(TKey key, out HashSet set) { set = new HashSet(ByteArrayEqualityComparer.Default); var path = key.ToArray().ToNibbles(); - if (path.Length == 0) return false; + if (path.Length < 1) return false; return GetProof(ref root, path, set); } @@ -23,7 +23,7 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) { case LeafNode leafNode: { - if (path.Length == 0) + if (path.Length < 1) { set.Add(leafNode.Encode()); return true; @@ -41,7 +41,7 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) case BranchNode branchNode: { set.Add(branchNode.Encode()); - if (path.Length == 0) + if (path.Length < 1) { return GetProof(ref branchNode.Children[BranchNode.ChildCount - 1], path, set); } diff --git a/src/neo/Trie/MPT/MPTTrie.Delete.cs b/src/neo/Trie/MPT/MPTTrie.Delete.cs index 6819b01dcd..d62c38e710 100644 --- a/src/neo/Trie/MPT/MPTTrie.Delete.cs +++ b/src/neo/Trie/MPT/MPTTrie.Delete.cs @@ -12,7 +12,7 @@ public partial class MPTTrie : MPTReadOnlyTrie public bool Delete(TKey key) { var path = key.ToArray().ToNibbles(); - if (path.Length == 0) return false; + if (path.Length < 1) return false; return TryDelete(ref root, path); } @@ -22,7 +22,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) { case LeafNode leafNode: { - if (path.Length == 0) + if (path.Length < 1) { node = HashNode.EmptyNode(); return true; @@ -54,7 +54,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) case BranchNode branchNode: { bool result; - if (path.Length == 0) + if (path.Length < 1) { result = TryDelete(ref branchNode.Children[BranchNode.ChildCount - 1], path); } diff --git a/src/neo/Trie/MPT/MPTTrie.Put.cs b/src/neo/Trie/MPT/MPTTrie.Put.cs index 8e120aee8b..3be803fd32 100644 --- a/src/neo/Trie/MPT/MPTTrie.Put.cs +++ b/src/neo/Trie/MPT/MPTTrie.Put.cs @@ -11,11 +11,11 @@ public bool Put(TKey key, TValue value) { var path = key.ToArray().ToNibbles(); var val = value.ToArray(); - if (ExtensionNode.MaxKeyLength < path.Length || path.Length == 0) + if (ExtensionNode.MaxKeyLength < path.Length || path.Length < 1) return false; if (LeafNode.MaxValueLength < val.Length) return false; - if (val.Length == 0) + if (val.Length < 1) return TryDelete(ref root, path); var n = new LeafNode(val); return Put(ref root, path, n); @@ -29,7 +29,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { if (val is LeafNode v) { - if (path.Length == 0) + if (path.Length < 1) { node = v; db.Put(node); @@ -66,7 +66,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) Put(ref grandSon1, keyRemain[1..], extensionNode.Next); son.Children[keyRemain[0]] = grandSon1; - if (pathRemain.Length == 0) + if (pathRemain.Length < 1) { Put(ref grandSon2, pathRemain, val); son.Children[BranchNode.ChildCount - 1] = grandSon2; @@ -96,7 +96,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) case BranchNode branchNode: { bool result; - if (path.Length == 0) + if (path.Length < 1) { result = Put(ref branchNode.Children[BranchNode.ChildCount - 1], path, val); } @@ -116,7 +116,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) MPTNode newNode; if (hashNode.IsEmptyNode) { - if (path.Length == 0) + if (path.Length < 1) { newNode = val; } From ca753a33297c501653b9bc9a1f09a9115bea6dcf Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 12 May 2020 12:47:23 +0800 Subject: [PATCH 123/171] Move namespace --- src/neo/{Trie/MPT/MPTNode => Cryptography/MPT}/BranchNode.cs | 2 +- .../{Trie/MPT/MPTNode => Cryptography/MPT}/ExtensionNode.cs | 2 +- src/neo/{Trie/MPT/MPTNode => Cryptography/MPT}/HashNode.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/Helper.cs | 2 +- src/neo/{Trie/MPT/MPTNode => Cryptography/MPT}/LeafNode.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/MPTDb.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/MPTNode.cs | 3 +-- src/neo/{Trie => Cryptography}/MPT/MPTNodeType.cs | 3 +-- src/neo/{Trie => Cryptography}/MPT/MPTProofStore.cs | 3 +-- src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyDb.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyTrie.Find.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyTrie.Get.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyTrie.Proof.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyTrie.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/MPTTrie.Delete.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/MPTTrie.Put.cs | 2 +- src/neo/{Trie => Cryptography}/MPT/MPTTrie.cs | 2 +- tests/neo.UnitTests/{Trie => Cryptography}/MPT/UT_Helper.cs | 4 ++-- tests/neo.UnitTests/{Trie => Cryptography}/MPT/UT_MPTNode.cs | 4 ++-- tests/neo.UnitTests/{Trie => Cryptography}/MPT/UT_MPTTrie.cs | 4 ++-- 20 files changed, 23 insertions(+), 26 deletions(-) rename src/neo/{Trie/MPT/MPTNode => Cryptography/MPT}/BranchNode.cs (97%) rename src/neo/{Trie/MPT/MPTNode => Cryptography/MPT}/ExtensionNode.cs (97%) rename src/neo/{Trie/MPT/MPTNode => Cryptography/MPT}/HashNode.cs (97%) rename src/neo/{Trie => Cryptography}/MPT/Helper.cs (96%) rename src/neo/{Trie/MPT/MPTNode => Cryptography/MPT}/LeafNode.cs (96%) rename src/neo/{Trie => Cryptography}/MPT/MPTDb.cs (94%) rename src/neo/{Trie => Cryptography}/MPT/MPTNode.cs (97%) rename src/neo/{Trie => Cryptography}/MPT/MPTNodeType.cs (82%) rename src/neo/{Trie => Cryptography}/MPT/MPTProofStore.cs (95%) rename src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyDb.cs (94%) rename src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyTrie.Find.cs (99%) rename src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyTrie.Get.cs (98%) rename src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyTrie.Proof.cs (98%) rename src/neo/{Trie => Cryptography}/MPT/MPTReadOnlyTrie.cs (96%) rename src/neo/{Trie => Cryptography}/MPT/MPTTrie.Delete.cs (99%) rename src/neo/{Trie => Cryptography}/MPT/MPTTrie.Put.cs (99%) rename src/neo/{Trie => Cryptography}/MPT/MPTTrie.cs (94%) rename tests/neo.UnitTests/{Trie => Cryptography}/MPT/UT_Helper.cs (95%) rename tests/neo.UnitTests/{Trie => Cryptography}/MPT/UT_MPTNode.cs (91%) rename tests/neo.UnitTests/{Trie => Cryptography}/MPT/UT_MPTTrie.cs (99%) diff --git a/src/neo/Trie/MPT/MPTNode/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs similarity index 97% rename from src/neo/Trie/MPT/MPTNode/BranchNode.cs rename to src/neo/Cryptography/MPT/BranchNode.cs index 298839c5dc..99956b0f99 100644 --- a/src/neo/Trie/MPT/MPTNode/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -1,7 +1,7 @@ using Neo.IO.Json; using System.IO; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public class BranchNode : MPTNode { diff --git a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs similarity index 97% rename from src/neo/Trie/MPT/MPTNode/ExtensionNode.cs rename to src/neo/Cryptography/MPT/ExtensionNode.cs index f1a46fea68..ddeb5e279f 100644 --- a/src/neo/Trie/MPT/MPTNode/ExtensionNode.cs +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -3,7 +3,7 @@ using Neo.SmartContract; using System.IO; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public class ExtensionNode : MPTNode { diff --git a/src/neo/Trie/MPT/MPTNode/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs similarity index 97% rename from src/neo/Trie/MPT/MPTNode/HashNode.cs rename to src/neo/Cryptography/MPT/HashNode.cs index 9780bd0935..8b135080fc 100644 --- a/src/neo/Trie/MPT/MPTNode/HashNode.cs +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -3,7 +3,7 @@ using System; using System.IO; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public class HashNode : MPTNode { diff --git a/src/neo/Trie/MPT/Helper.cs b/src/neo/Cryptography/MPT/Helper.cs similarity index 96% rename from src/neo/Trie/MPT/Helper.cs rename to src/neo/Cryptography/MPT/Helper.cs index ce4627c917..760aadd7e7 100644 --- a/src/neo/Trie/MPT/Helper.cs +++ b/src/neo/Cryptography/MPT/Helper.cs @@ -1,6 +1,6 @@ using System; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { internal static class Helper { diff --git a/src/neo/Trie/MPT/MPTNode/LeafNode.cs b/src/neo/Cryptography/MPT/LeafNode.cs similarity index 96% rename from src/neo/Trie/MPT/MPTNode/LeafNode.cs rename to src/neo/Cryptography/MPT/LeafNode.cs index 8620d5f5cf..b6ab2294a0 100644 --- a/src/neo/Trie/MPT/MPTNode/LeafNode.cs +++ b/src/neo/Cryptography/MPT/LeafNode.cs @@ -3,7 +3,7 @@ using Neo.SmartContract; using System.IO; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public class LeafNode : MPTNode { diff --git a/src/neo/Trie/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs similarity index 94% rename from src/neo/Trie/MPT/MPTDb.cs rename to src/neo/Cryptography/MPT/MPTDb.cs index d4adac8747..3630286544 100644 --- a/src/neo/Trie/MPT/MPTDb.cs +++ b/src/neo/Cryptography/MPT/MPTDb.cs @@ -1,7 +1,7 @@ using Neo.IO; using Neo.Persistence; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public class MPTDb : MPTReadOnlyDb { diff --git a/src/neo/Trie/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs similarity index 97% rename from src/neo/Trie/MPT/MPTNode.cs rename to src/neo/Cryptography/MPT/MPTNode.cs index 52803bcc24..b07f1c247a 100644 --- a/src/neo/Trie/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -1,9 +1,8 @@ -using Neo.Cryptography; using Neo.IO.Json; using System.IO; using System.Text; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public abstract class MPTNode { diff --git a/src/neo/Trie/MPT/MPTNodeType.cs b/src/neo/Cryptography/MPT/MPTNodeType.cs similarity index 82% rename from src/neo/Trie/MPT/MPTNodeType.cs rename to src/neo/Cryptography/MPT/MPTNodeType.cs index d9b20c3393..90084bb790 100644 --- a/src/neo/Trie/MPT/MPTNodeType.cs +++ b/src/neo/Cryptography/MPT/MPTNodeType.cs @@ -1,5 +1,4 @@ - -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public enum NodeType { diff --git a/src/neo/Trie/MPT/MPTProofStore.cs b/src/neo/Cryptography/MPT/MPTProofStore.cs similarity index 95% rename from src/neo/Trie/MPT/MPTProofStore.cs rename to src/neo/Cryptography/MPT/MPTProofStore.cs index a237e97dfc..2227c3412e 100644 --- a/src/neo/Trie/MPT/MPTProofStore.cs +++ b/src/neo/Cryptography/MPT/MPTProofStore.cs @@ -1,9 +1,8 @@ -using Neo.Cryptography; using Neo.Persistence; using System; using System.Collections.Generic; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public class MPTProofStore : IReadOnlyStore { diff --git a/src/neo/Trie/MPT/MPTReadOnlyDb.cs b/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs similarity index 94% rename from src/neo/Trie/MPT/MPTReadOnlyDb.cs rename to src/neo/Cryptography/MPT/MPTReadOnlyDb.cs index 77ea99b039..8fec7087b3 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyDb.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs @@ -1,7 +1,7 @@ using Neo.IO; using Neo.Persistence; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public class MPTReadOnlyDb { diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs similarity index 99% rename from src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs rename to src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs index bc4b158cb0..a5e3fbcb31 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using static Neo.Helper; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public partial class MPTReadOnlyTrie where TKey : notnull, ISerializable, new() diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs similarity index 98% rename from src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs rename to src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs index a92ea69bf6..bc46129454 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Get.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs @@ -1,7 +1,7 @@ using Neo.IO; using System; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public partial class MPTReadOnlyTrie where TKey : notnull, ISerializable, new() diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs similarity index 98% rename from src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs rename to src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs index 73c487d560..224415d1a5 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public partial class MPTReadOnlyTrie where TKey : notnull, ISerializable, new() diff --git a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs similarity index 96% rename from src/neo/Trie/MPT/MPTReadOnlyTrie.cs rename to src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs index e16de54b05..c211740bbf 100644 --- a/src/neo/Trie/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs @@ -1,7 +1,7 @@ using Neo.IO; using Neo.Persistence; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public partial class MPTReadOnlyTrie where TKey : notnull, ISerializable, new() diff --git a/src/neo/Trie/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs similarity index 99% rename from src/neo/Trie/MPT/MPTTrie.Delete.cs rename to src/neo/Cryptography/MPT/MPTTrie.Delete.cs index d62c38e710..407c4e814b 100644 --- a/src/neo/Trie/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using static Neo.Helper; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public partial class MPTTrie : MPTReadOnlyTrie where TKey : notnull, ISerializable, new() diff --git a/src/neo/Trie/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs similarity index 99% rename from src/neo/Trie/MPT/MPTTrie.Put.cs rename to src/neo/Cryptography/MPT/MPTTrie.Put.cs index 3be803fd32..83b2e2bda2 100644 --- a/src/neo/Trie/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -1,7 +1,7 @@ using Neo.IO; using System; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public partial class MPTTrie : MPTReadOnlyTrie where TKey : notnull, ISerializable, new() diff --git a/src/neo/Trie/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs similarity index 94% rename from src/neo/Trie/MPT/MPTTrie.cs rename to src/neo/Cryptography/MPT/MPTTrie.cs index 2b5f23ba6a..029bf530e8 100644 --- a/src/neo/Trie/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -2,7 +2,7 @@ using Neo.IO.Json; using Neo.Persistence; -namespace Neo.Trie.MPT +namespace Neo.Cryptography.MPT { public partial class MPTTrie : MPTReadOnlyTrie where TKey : notnull, ISerializable, new() diff --git a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs similarity index 95% rename from tests/neo.UnitTests/Trie/MPT/UT_Helper.cs rename to tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs index 86b11e3f68..b66e0546e4 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs @@ -1,7 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Trie.MPT; +using Neo.Cryptography.MPT; -namespace Neo.UnitTests.Trie.MPT +namespace Neo.UnitTests.Cryptography.MPT { [TestClass] public class UT_Helper diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs similarity index 91% rename from tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs rename to tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs index 3b77f63246..6b15340896 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTNode.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs @@ -1,8 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Trie.MPT; +using Neo.Cryptography.MPT; using System.Text; -namespace Neo.UnitTests.Trie.MPT +namespace Neo.UnitTests.Cryptography.MPT { [TestClass] public class UT_MPTNode diff --git a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs similarity index 99% rename from tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs rename to tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index bbb1781f36..0a1019d495 100644 --- a/tests/neo.UnitTests/Trie/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -1,14 +1,14 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.MPT; using Neo.IO; using Neo.Persistence; -using Neo.Trie.MPT; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -namespace Neo.UnitTests.Trie.MPT +namespace Neo.UnitTests.Cryptography.MPT { public class TestKey : ISerializable { From dbfff2c87fb39d645d362a5e6311e5a7d2798436 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 12 May 2020 12:51:14 +0800 Subject: [PATCH 124/171] Move Helper.CommonPrefix() --- src/neo/Cryptography/MPT/Helper.cs | 15 -------- src/neo/Cryptography/MPT/MPTTrie.Put.cs | 17 +++++++++- .../Cryptography/MPT/UT_Helper.cs | 34 ------------------- 3 files changed, 16 insertions(+), 50 deletions(-) diff --git a/src/neo/Cryptography/MPT/Helper.cs b/src/neo/Cryptography/MPT/Helper.cs index 760aadd7e7..5713edfa88 100644 --- a/src/neo/Cryptography/MPT/Helper.cs +++ b/src/neo/Cryptography/MPT/Helper.cs @@ -4,21 +4,6 @@ namespace Neo.Cryptography.MPT { internal static class Helper { - public static byte[] CommonPrefix(this byte[] a, byte[] b) - { - if (a is null || b is null) return Array.Empty(); - var minLen = a.Length <= b.Length ? a.Length : b.Length; - int i = 0; - if (a.Length != 0 && b.Length != 0) - { - for (i = 0; i < minLen; i++) - { - if (a[i] != b[i]) break; - } - } - return a[..i]; - } - public static byte[] ToNibbles(this byte[] path) { if (path is null) return Array.Empty(); diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs index 83b2e2bda2..0c85cfe99d 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -7,6 +7,21 @@ public partial class MPTTrie : MPTReadOnlyTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { + private static byte[] CommonPrefix(byte[] a, byte[] b) + { + if (a is null || b is null) return Array.Empty(); + var minLen = a.Length <= b.Length ? a.Length : b.Length; + int i = 0; + if (a.Length != 0 && b.Length != 0) + { + for (i = 0; i < minLen; i++) + { + if (a[i] != b[i]) break; + } + } + return a[..i]; + } + public bool Put(TKey key, TValue value) { var path = key.ToArray().ToNibbles(); @@ -56,7 +71,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } return result; } - var prefix = extensionNode.Key.CommonPrefix(path); + var prefix = CommonPrefix(extensionNode.Key, path); var pathRemain = path[prefix.Length..]; var keyRemain = extensionNode.Key[prefix.Length..]; var son = new BranchNode(); diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs index b66e0546e4..3ab0fcf1fb 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs @@ -6,40 +6,6 @@ namespace Neo.UnitTests.Cryptography.MPT [TestClass] public class UT_Helper { - [TestMethod] - public void TestCommonPrefix() - { - var a = "1234abcd".HexToBytes(); - var b = "".HexToBytes(); - var prefix = a.CommonPrefix(b); - Assert.IsTrue(prefix.Length == 0); - - b = "100000".HexToBytes(); - prefix = a.CommonPrefix(b); - Assert.IsTrue(prefix.Length == 0); - - b = "1234".HexToBytes(); - prefix = a.CommonPrefix(b); - Assert.AreEqual("1234", prefix.ToHexString()); - - b = a; - prefix = a.CommonPrefix(b); - Assert.AreEqual("1234abcd", prefix.ToHexString()); - - a = new byte[0]; - b = new byte[0]; - prefix = a.CommonPrefix(b); - Assert.IsTrue(prefix.Length == 0); - - a = "1234abcd".HexToBytes(); - b = null; - Assert.AreEqual(0, a.CommonPrefix(b).Length); - - a = null; - b = null; - Assert.AreEqual(0, a.CommonPrefix(b).Length); - } - [TestMethod] public void TestToNibbles() { From dc54eef51d25b81397ca17736eeaa47d5f6eaa7e Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 12 May 2020 13:08:22 +0800 Subject: [PATCH 125/171] Remove Helper.cs --- src/neo/Cryptography/MPT/Helper.cs | 19 ------------------ .../Cryptography/MPT/MPTReadOnlyTrie.Find.cs | 2 +- .../Cryptography/MPT/MPTReadOnlyTrie.Get.cs | 2 +- .../Cryptography/MPT/MPTReadOnlyTrie.Proof.cs | 2 +- src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs | 12 +++++++++++ src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 2 +- src/neo/Cryptography/MPT/MPTTrie.Put.cs | 7 +++---- .../Cryptography/MPT/UT_Helper.cs | 20 ------------------- 8 files changed, 19 insertions(+), 47 deletions(-) delete mode 100644 src/neo/Cryptography/MPT/Helper.cs delete mode 100644 tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs diff --git a/src/neo/Cryptography/MPT/Helper.cs b/src/neo/Cryptography/MPT/Helper.cs deleted file mode 100644 index 5713edfa88..0000000000 --- a/src/neo/Cryptography/MPT/Helper.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Neo.Cryptography.MPT -{ - internal static class Helper - { - public static byte[] ToNibbles(this byte[] path) - { - if (path is null) return Array.Empty(); - var result = new byte[path.Length * 2]; - for (int i = 0; i < path.Length; i++) - { - result[i * 2] = (byte)(path[i] >> 4); - result[i * 2 + 1] = (byte)(path[i] & 0x0F); - } - return result; - } - } -} diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs index a5e3fbcb31..d711cc3c46 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs @@ -64,7 +64,7 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) public IEnumerable<(TKey Key, TValue Value)> Find(byte[] prefix) { - var path = prefix.ToNibbles(); + var path = ToNibbles(prefix); path = Seek(ref root, path, out MPTNode start); foreach (var item in Travers(start, path)) yield return (item.Key.AsSerializable(), item.Value.AsSerializable()); diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs index bc46129454..8b1f74b5d5 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs @@ -9,7 +9,7 @@ public partial class MPTReadOnlyTrie { public TValue Get(TKey key) { - var path = key.ToArray().ToNibbles(); + var path = ToNibbles(key.ToArray()); if (path.Length < 1) return null; var result = TryGet(ref root, path, out byte[] value); return result ? value.AsSerializable() : null; diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs index 224415d1a5..7625ce8427 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs @@ -12,7 +12,7 @@ public partial class MPTReadOnlyTrie public bool GetProof(TKey key, out HashSet set) { set = new HashSet(ByteArrayEqualityComparer.Default); - var path = key.ToArray().ToNibbles(); + var path = ToNibbles(key.ToArray()); if (path.Length < 1) return false; return GetProof(ref root, path, set); } diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs index c211740bbf..47e97cc045 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs @@ -1,5 +1,6 @@ using Neo.IO; using Neo.Persistence; +using System; namespace Neo.Cryptography.MPT { @@ -36,5 +37,16 @@ public UInt256 GetRoot() { return root.GetHash(); } + + protected static byte[] ToNibbles(ReadOnlySpan path) + { + var result = new byte[path.Length * 2]; + for (int i = 0; i < path.Length; i++) + { + result[i * 2] = (byte)(path[i] >> 4); + result[i * 2 + 1] = (byte)(path[i] & 0x0F); + } + return result; + } } } diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index 407c4e814b..abda16f98f 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -11,7 +11,7 @@ public partial class MPTTrie : MPTReadOnlyTrie { public bool Delete(TKey key) { - var path = key.ToArray().ToNibbles(); + var path = ToNibbles(key.ToArray()); if (path.Length < 1) return false; return TryDelete(ref root, path); } diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs index 0c85cfe99d..face10a645 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -7,9 +7,8 @@ public partial class MPTTrie : MPTReadOnlyTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { - private static byte[] CommonPrefix(byte[] a, byte[] b) + private static ReadOnlySpan CommonPrefix(ReadOnlySpan a, ReadOnlySpan b) { - if (a is null || b is null) return Array.Empty(); var minLen = a.Length <= b.Length ? a.Length : b.Length; int i = 0; if (a.Length != 0 && b.Length != 0) @@ -24,7 +23,7 @@ private static byte[] CommonPrefix(byte[] a, byte[] b) public bool Put(TKey key, TValue value) { - var path = key.ToArray().ToNibbles(); + var path = ToNibbles(key.ToArray()); var val = value.ToArray(); if (ExtensionNode.MaxKeyLength < path.Length || path.Length < 1) return false; @@ -96,7 +95,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { var exNode = new ExtensionNode() { - Key = prefix, + Key = prefix.ToArray(), Next = son, }; db.Put(exNode); diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs deleted file mode 100644 index 3ab0fcf1fb..0000000000 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_Helper.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Cryptography.MPT; - -namespace Neo.UnitTests.Cryptography.MPT -{ - [TestClass] - public class UT_Helper - { - [TestMethod] - public void TestToNibbles() - { - var a = "1234abcd".HexToBytes(); - var n = a.ToNibbles(); - Assert.AreEqual("010203040a0b0c0d", n.ToHexString()); - - a = null; - Assert.AreEqual(0, a.ToNibbles().Length); - } - } -} From 48d0b616a1ef5f2fcc0c793603bb9c1a24875ffa Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 12 May 2020 13:27:33 +0800 Subject: [PATCH 126/171] optimize --- src/neo/Cryptography/MPT/LeafNode.cs | 5 ++-- src/neo/Cryptography/MPT/MPTNode.cs | 30 +++++++++++++---------- src/neo/Cryptography/MPT/MPTNodeType.cs | 2 +- src/neo/Cryptography/MPT/MPTProofStore.cs | 4 +-- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/neo/Cryptography/MPT/LeafNode.cs b/src/neo/Cryptography/MPT/LeafNode.cs index b6ab2294a0..3cef6d2c5e 100644 --- a/src/neo/Cryptography/MPT/LeafNode.cs +++ b/src/neo/Cryptography/MPT/LeafNode.cs @@ -1,6 +1,7 @@ using Neo.IO; using Neo.IO.Json; using Neo.SmartContract; +using System; using System.IO; namespace Neo.Cryptography.MPT @@ -17,9 +18,9 @@ public LeafNode() nType = NodeType.LeafNode; } - public LeafNode(byte[] val) : this() + public LeafNode(ReadOnlySpan val) : this() { - Value = (byte[])val.Clone(); + Value = val.ToArray(); } public override void EncodeSpecific(BinaryWriter writer) diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index b07f1c247a..8fbcb22238 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -1,4 +1,5 @@ using Neo.IO.Json; +using System; using System.IO; using System.Text; @@ -47,23 +48,26 @@ public byte[] Encode() public abstract void EncodeSpecific(BinaryWriter writer); - public static MPTNode Decode(byte[] data) + public static unsafe MPTNode Decode(ReadOnlySpan data) { - if (data is null || data.Length < 1) - return null; + if (data.Length < 1) return null; - using BinaryReader reader = new BinaryReader(new MemoryStream(data, false), Encoding.UTF8, false); - - var n = (NodeType)reader.ReadByte() switch + fixed (byte* pointer = data) { - NodeType.BranchNode => (MPTNode)new BranchNode(), - NodeType.ExtensionNode => new ExtensionNode(), - NodeType.LeafNode => new LeafNode(), - _ => throw new System.InvalidOperationException(), - }; + using UnmanagedMemoryStream stream = new UnmanagedMemoryStream(pointer, data.Length); + using BinaryReader reader = new BinaryReader(stream); + + var n = (NodeType)reader.ReadByte() switch + { + NodeType.BranchNode => (MPTNode)new BranchNode(), + NodeType.ExtensionNode => new ExtensionNode(), + NodeType.LeafNode => new LeafNode(), + _ => throw new InvalidOperationException(), + }; - n.DecodeSpecific(reader); - return n; + n.DecodeSpecific(reader); + return n; + } } public abstract void DecodeSpecific(BinaryReader reader); diff --git a/src/neo/Cryptography/MPT/MPTNodeType.cs b/src/neo/Cryptography/MPT/MPTNodeType.cs index 90084bb790..657b9ba4d9 100644 --- a/src/neo/Cryptography/MPT/MPTNodeType.cs +++ b/src/neo/Cryptography/MPT/MPTNodeType.cs @@ -1,6 +1,6 @@ namespace Neo.Cryptography.MPT { - public enum NodeType + public enum NodeType : byte { BranchNode = 0x00, ExtensionNode = 0x01, diff --git a/src/neo/Cryptography/MPT/MPTProofStore.cs b/src/neo/Cryptography/MPT/MPTProofStore.cs index 2227c3412e..2090d5ef28 100644 --- a/src/neo/Cryptography/MPT/MPTProofStore.cs +++ b/src/neo/Cryptography/MPT/MPTProofStore.cs @@ -6,9 +6,9 @@ namespace Neo.Cryptography.MPT { public class MPTProofStore : IReadOnlyStore { - private Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); + private readonly Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); - public MPTProofStore(HashSet proof) + public MPTProofStore(IEnumerable proof) { foreach (byte[] data in proof) { From 69b2fcc899a0fd6f999491bd33db062e1ec6a2b7 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:10:44 +0200 Subject: [PATCH 127/171] Update src/neo/Cryptography/MPT/BranchNode.cs --- src/neo/Cryptography/MPT/BranchNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs index 99956b0f99..c0cf5d30ee 100644 --- a/src/neo/Cryptography/MPT/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -6,7 +6,7 @@ namespace Neo.Cryptography.MPT public class BranchNode : MPTNode { public const int ChildCount = 17; - public MPTNode[] Children = new MPTNode[ChildCount]; + public readonly MPTNode[] Children = new MPTNode[ChildCount]; public BranchNode() { From d4c21ea15150ff6cd0339d25968318abbf3e55ff Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:10:51 +0200 Subject: [PATCH 128/171] Update src/neo/Cryptography/MPT/MPTReadOnlyDb.cs --- src/neo/Cryptography/MPT/MPTReadOnlyDb.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs b/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs index 8fec7087b3..1c7db457bf 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs @@ -5,7 +5,7 @@ namespace Neo.Cryptography.MPT { public class MPTReadOnlyDb { - private IReadOnlyStore store; + private readonly IReadOnlyStore store; protected byte prefix; public MPTReadOnlyDb(IReadOnlyStore store, byte prefix) From 99e7040c8ff925d905f0353ac2079ff11dbf20ca Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:10:59 +0200 Subject: [PATCH 129/171] Update src/neo/Cryptography/MPT/MPTReadOnlyDb.cs --- src/neo/Cryptography/MPT/MPTReadOnlyDb.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs b/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs index 1c7db457bf..7a91745e4e 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs @@ -6,7 +6,7 @@ namespace Neo.Cryptography.MPT public class MPTReadOnlyDb { private readonly IReadOnlyStore store; - protected byte prefix; + protected readonly byte prefix; public MPTReadOnlyDb(IReadOnlyStore store, byte prefix) { From 359ba3740e41dce62dcda41e4ce379b99bfed7ab Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:11:07 +0200 Subject: [PATCH 130/171] Update src/neo/Cryptography/MPT/MPTTrie.cs --- 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 029bf530e8..f92af2ce70 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -8,7 +8,7 @@ public partial class MPTTrie : MPTReadOnlyTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { - private MPTDb db; + private readonly MPTDb db; public MPTTrie(UInt256 root, ISnapshot store, byte prefix) : base(root, store, prefix) { From bd1284ed1e832b67436906a14bac0fe9fe7e8a91 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:11:18 +0200 Subject: [PATCH 131/171] Update src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs --- src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs index 47e97cc045..7491b9ff05 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs @@ -9,7 +9,7 @@ public partial class MPTReadOnlyTrie where TValue : class, ISerializable, new() { private MPTReadOnlyDb rodb; - protected MPTNode root; + protected readonly MPTNode root; public MPTReadOnlyTrie(UInt256 root, IReadOnlyStore store, byte prefix) { From 8f3b06933284f19f70b8cbd12ad7769950597a2d Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:11:26 +0200 Subject: [PATCH 132/171] Update src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs --- src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs index 7491b9ff05..b5e785374d 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs @@ -8,7 +8,7 @@ public partial class MPTReadOnlyTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { - private MPTReadOnlyDb rodb; + private readonly MPTReadOnlyDb rodb; protected readonly MPTNode root; public MPTReadOnlyTrie(UInt256 root, IReadOnlyStore store, byte prefix) From d01b3ed0061dcdcc292d189807cd4f8b6177f2b7 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:11:33 +0200 Subject: [PATCH 133/171] Update src/neo/Cryptography/MPT/MPTDb.cs --- src/neo/Cryptography/MPT/MPTDb.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs index 3630286544..b3fa937389 100644 --- a/src/neo/Cryptography/MPT/MPTDb.cs +++ b/src/neo/Cryptography/MPT/MPTDb.cs @@ -5,7 +5,7 @@ namespace Neo.Cryptography.MPT { public class MPTDb : MPTReadOnlyDb { - private ISnapshot store; + private readonly ISnapshot store; public MPTDb(ISnapshot store, byte prefix) : base(store, prefix) { From 6e1a4b7e735368a142158024c3069cea7af87ae9 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:19:42 +0200 Subject: [PATCH 134/171] Fix UT --- src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs index b5e785374d..9d3fd45ea2 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs @@ -9,7 +9,7 @@ public partial class MPTReadOnlyTrie where TValue : class, ISerializable, new() { private readonly MPTReadOnlyDb rodb; - protected readonly MPTNode root; + protected MPTNode root; public MPTReadOnlyTrie(UInt256 root, IReadOnlyStore store, byte prefix) { From ad13492cea320d9672cb970edd0a6040f6448d30 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:23:04 +0200 Subject: [PATCH 135/171] Some VS recommendations --- src/neo/Cryptography/MPT/MPTDb.cs | 2 +- src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs | 4 ++-- src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs | 2 +- src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs index b3fa937389..3d3f54c958 100644 --- a/src/neo/Cryptography/MPT/MPTDb.cs +++ b/src/neo/Cryptography/MPT/MPTDb.cs @@ -14,7 +14,7 @@ public MPTDb(ISnapshot store, byte prefix) : base(store, prefix) public void Put(MPTNode node) { - if (node is HashNode hn) + if (node is HashNode) throw new System.InvalidOperationException("Means nothing to store HashNode"); store.Put(prefix, node.GetHash().ToArray(), node.Encode()); } diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs index d711cc3c46..c9eb72b580 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs @@ -66,8 +66,8 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) { var path = ToNibbles(prefix); path = Seek(ref root, path, out MPTNode start); - foreach (var item in Travers(start, path)) - yield return (item.Key.AsSerializable(), item.Value.AsSerializable()); + foreach (var (Key, Value) in Travers(start, path)) + yield return (Key.AsSerializable(), Value.AsSerializable()); } private IEnumerable<(byte[] Key, byte[] Value)> Travers(MPTNode node, byte[] path) diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs index 9d3fd45ea2..32c4d67bf4 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs @@ -14,7 +14,7 @@ public partial class MPTReadOnlyTrie public MPTReadOnlyTrie(UInt256 root, IReadOnlyStore store, byte prefix) { if (store is null) - throw new System.ArgumentNullException(); + throw new ArgumentNullException(); this.rodb = new MPTReadOnlyDb(store, prefix); diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index abda16f98f..338d2f6785 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -20,7 +20,8 @@ private bool TryDelete(ref MPTNode node, byte[] path) { switch (node) { - case LeafNode leafNode: + case LeafNode _: + { if (path.Length < 1) { From fca08842b213cdac00575c395730560c2240668b Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 May 2020 10:25:46 +0200 Subject: [PATCH 136/171] dotnet format --- src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index 338d2f6785..2b1c12a40d 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -21,7 +21,6 @@ private bool TryDelete(ref MPTNode node, byte[] path) switch (node) { case LeafNode _: - { if (path.Length < 1) { From 70d080430e46fd21d8e54e4136bec81e2cab7486 Mon Sep 17 00:00:00 2001 From: Tommo-L Date: Fri, 15 May 2020 09:49:46 +0800 Subject: [PATCH 137/171] Order the result value of Find method --- src/neo/Cryptography/MPT/MPTProofStore.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTProofStore.cs b/src/neo/Cryptography/MPT/MPTProofStore.cs index 2090d5ef28..9805b8e476 100644 --- a/src/neo/Cryptography/MPT/MPTProofStore.cs +++ b/src/neo/Cryptography/MPT/MPTProofStore.cs @@ -1,6 +1,8 @@ +using Neo.IO; using Neo.Persistence; using System; using System.Collections.Generic; +using System.Linq; namespace Neo.Cryptography.MPT { @@ -24,13 +26,12 @@ public byte[] TryGet(byte prefix, byte[] hash) public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) { - foreach (var pair in store) - { - if (prefix is null || pair.Key.AsSpan().StartsWith(prefix)) - { - yield return (pair.Key, pair.Value); - } - } + IEnumerable> records = store; + if (prefix?.Length > 0) + records = records.Where(p => p.Key.AsSpan().StartsWith(prefix)); + records = records.OrderBy(p => p.Key, ByteArrayComparer.Default); + foreach (var pair in records) + yield return (pair.Key, pair.Value); } } } From 5b2462d688ea5fa1e40cf0e16bd73edbfe61a442 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 15 May 2020 18:39:41 +0800 Subject: [PATCH 138/171] Optimize --- .../Cryptography/MPT/MPTReadOnlyTrie.Find.cs | 21 ++++++++++--------- .../Cryptography/MPT/MPTReadOnlyTrie.Get.cs | 10 ++++----- .../Cryptography/MPT/MPTReadOnlyTrie.Proof.cs | 4 ++-- src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 4 ++-- src/neo/Cryptography/MPT/MPTTrie.Put.cs | 8 +++---- src/neo/Helper.cs | 8 +++++++ .../Cryptography/MPT/UT_MPTTrie.cs | 4 ++-- 7 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs index c9eb72b580..f963a21944 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs @@ -1,6 +1,7 @@ using Neo.IO; using System; using System.Collections.Generic; +using System.Linq; using static Neo.Helper; namespace Neo.Cryptography.MPT @@ -9,7 +10,7 @@ public partial class MPTReadOnlyTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { - private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) + private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out MPTNode start) { switch (node) { @@ -18,7 +19,7 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) if (path.Length < 1) { start = leafNode; - return Array.Empty(); + return ReadOnlySpan.Empty; } break; } @@ -35,7 +36,7 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) if (path.Length < 1) { start = branchNode; - return Array.Empty(); + return ReadOnlySpan.Empty; } return Concat(path[..1], Seek(ref branchNode.Children[path[0]], path[1..], out start)); } @@ -44,9 +45,9 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) if (path.Length < 1) { start = extensionNode; - return Array.Empty(); + return ReadOnlySpan.Empty; } - if (path.AsSpan().StartsWith(extensionNode.Key)) + if (path.StartsWith(extensionNode.Key)) { return Concat(extensionNode.Key, Seek(ref extensionNode.Next, path[extensionNode.Key.Length..], out start)); } @@ -59,15 +60,15 @@ private byte[] Seek(ref MPTNode node, byte[] path, out MPTNode start) } } start = null; - return Array.Empty(); + return ReadOnlySpan.Empty; } - public IEnumerable<(TKey Key, TValue Value)> Find(byte[] prefix) + public IEnumerable<(TKey Key, TValue Value)> Find(ReadOnlySpan prefix) { var path = ToNibbles(prefix); - path = Seek(ref root, path, out MPTNode start); - foreach (var (Key, Value) in Travers(start, path)) - yield return (Key.AsSerializable(), Value.AsSerializable()); + path = Seek(ref root, path, out MPTNode start).ToArray(); + return Travers(start, path) + .Select(p => (p.Key.AsSerializable(), p.Value.AsSerializable())); } private IEnumerable<(byte[] Key, byte[] Value)> Travers(MPTNode node, byte[] path) diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs index 8b1f74b5d5..efa3fb4bab 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs @@ -11,11 +11,11 @@ public TValue Get(TKey key) { var path = ToNibbles(key.ToArray()); if (path.Length < 1) return null; - var result = TryGet(ref root, path, out byte[] value); + var result = TryGet(ref root, path, out var value); return result ? value.AsSerializable() : null; } - private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) + private bool TryGet(ref MPTNode node, ReadOnlySpan path, out ReadOnlySpan value) { switch (node) { @@ -23,7 +23,7 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) { if (path.Length < 1) { - value = (byte[])leafNode.Value.Clone(); + value = leafNode.Value; return true; } break; @@ -46,14 +46,14 @@ private bool TryGet(ref MPTNode node, byte[] path, out byte[] value) } case ExtensionNode extensionNode: { - if (path.AsSpan().StartsWith(extensionNode.Key)) + if (path.StartsWith(extensionNode.Key)) { return TryGet(ref extensionNode.Next, path[extensionNode.Key.Length..], out value); } break; } } - value = null; + value = default; return false; } diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs index 7625ce8427..41ab324cac 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs @@ -17,7 +17,7 @@ public bool GetProof(TKey key, out HashSet set) return GetProof(ref root, path, set); } - private bool GetProof(ref MPTNode node, byte[] path, HashSet set) + private bool GetProof(ref MPTNode node, ReadOnlySpan path, HashSet set) { switch (node) { @@ -49,7 +49,7 @@ private bool GetProof(ref MPTNode node, byte[] path, HashSet set) } case ExtensionNode extensionNode: { - if (path.AsSpan().StartsWith(extensionNode.Key)) + if (path.StartsWith(extensionNode.Key)) { set.Add(extensionNode.Encode()); return GetProof(ref extensionNode.Next, path[extensionNode.Key.Length..], set); diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index 2b1c12a40d..c27607e7e3 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -16,7 +16,7 @@ public bool Delete(TKey key) return TryDelete(ref root, path); } - private bool TryDelete(ref MPTNode node, byte[] path) + private bool TryDelete(ref MPTNode node, ReadOnlySpan path) { switch (node) { @@ -31,7 +31,7 @@ private bool TryDelete(ref MPTNode node, byte[] path) } case ExtensionNode extensionNode: { - if (path.AsSpan().StartsWith(extensionNode.Key)) + if (path.StartsWith(extensionNode.Key)) { var result = TryDelete(ref extensionNode.Next, path[extensionNode.Key.Length..]); if (!result) return false; diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs index face10a645..7eaca9cd31 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -35,7 +35,7 @@ public bool Put(TKey key, TValue value) return Put(ref root, path, n); } - private bool Put(ref MPTNode node, byte[] path, MPTNode val) + private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) { switch (node) { @@ -60,7 +60,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } case ExtensionNode extensionNode: { - if (path.AsSpan().StartsWith(extensionNode.Key)) + if (path.StartsWith(extensionNode.Key)) { var result = Put(ref extensionNode.Next, path[extensionNode.Key.Length..], val); if (result) @@ -72,7 +72,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) } var prefix = CommonPrefix(extensionNode.Key, path); var pathRemain = path[prefix.Length..]; - var keyRemain = extensionNode.Key[prefix.Length..]; + var keyRemain = extensionNode.Key.AsSpan(prefix.Length); var son = new BranchNode(); MPTNode grandSon1 = HashNode.EmptyNode(); MPTNode grandSon2 = HashNode.EmptyNode(); @@ -138,7 +138,7 @@ private bool Put(ref MPTNode node, byte[] path, MPTNode val) { newNode = new ExtensionNode() { - Key = path, + Key = path.ToArray(), Next = val, }; db.Put(newNode); diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index b8f185e2c9..dafeba7643 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -50,6 +50,14 @@ public static byte[] Concat(params byte[][] buffers) return dst; } + public static byte[] Concat(ReadOnlySpan a, ReadOnlySpan b) + { + byte[] buffer = new byte[a.Length + b.Length]; + a.CopyTo(buffer); + b.CopyTo(buffer.AsSpan(a.Length)); + return buffer; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetBitLength(this BigInteger i) { diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index 0a1019d495..2fe626e6c9 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -354,13 +354,13 @@ public void TestFind() var store = new MemoryStore(); var snapshot = store.GetSnapshot(); var mpt1 = new MPTTrie(null, snapshot, 0); - var results = mpt1.Find(Array.Empty()).ToArray(); + var results = mpt1.Find(ReadOnlySpan.Empty).ToArray(); Assert.AreEqual(0, results.Count()); var mpt2 = new MPTTrie(null, snapshot, 0); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xef }, new byte[] { 0x01 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xe1 }, new byte[] { 0x02 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x03 })); - results = mpt2.Find(Array.Empty()).ToArray(); + results = mpt2.Find(ReadOnlySpan.Empty).ToArray(); Assert.AreEqual(3, results.Count()); results = mpt2.Find(new byte[] { 0xab }).ToArray(); Assert.AreEqual(3, results.Count()); From 01f3b76c1f4f9f888ec330a1ac1206f9de957205 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 15:04:28 +0800 Subject: [PATCH 139/171] Optimize --- src/neo/Cryptography/MPT/BranchNode.cs | 3 ++- src/neo/Cryptography/MPT/ExtensionNode.cs | 5 +---- src/neo/Cryptography/MPT/HashNode.cs | 5 +++-- src/neo/Cryptography/MPT/LeafNode.cs | 6 +++--- src/neo/Cryptography/MPT/MPTNode.cs | 4 ++-- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs index c0cf5d30ee..b91a3e0f7e 100644 --- a/src/neo/Cryptography/MPT/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -8,9 +8,10 @@ public class BranchNode : MPTNode public const int ChildCount = 17; public readonly MPTNode[] Children = new MPTNode[ChildCount]; + protected override NodeType Type => NodeType.BranchNode; + public BranchNode() { - nType = NodeType.BranchNode; for (int i = 0; i < ChildCount; i++) { Children[i] = HashNode.EmptyNode(); diff --git a/src/neo/Cryptography/MPT/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs index ddeb5e279f..95424857d2 100644 --- a/src/neo/Cryptography/MPT/ExtensionNode.cs +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -12,10 +12,7 @@ public class ExtensionNode : MPTNode public byte[] Key; public MPTNode Next; - public ExtensionNode() - { - nType = NodeType.ExtensionNode; - } + protected override NodeType Type => NodeType.ExtensionNode; public override void EncodeSpecific(BinaryWriter writer) { diff --git a/src/neo/Cryptography/MPT/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs index 8b135080fc..a13cf156c8 100644 --- a/src/neo/Cryptography/MPT/HashNode.cs +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -9,12 +9,13 @@ public class HashNode : MPTNode { public UInt256 Hash; + protected override NodeType Type => NodeType.HashNode; + public HashNode() { - nType = NodeType.HashNode; } - public HashNode(UInt256 hash) : this() + public HashNode(UInt256 hash) { Hash = hash; } diff --git a/src/neo/Cryptography/MPT/LeafNode.cs b/src/neo/Cryptography/MPT/LeafNode.cs index 3cef6d2c5e..b02c71f793 100644 --- a/src/neo/Cryptography/MPT/LeafNode.cs +++ b/src/neo/Cryptography/MPT/LeafNode.cs @@ -10,15 +10,15 @@ public class LeafNode : MPTNode { //the max size when store StorageItem public const int MaxValueLength = 3 + InteropService.Storage.MaxValueSize + sizeof(bool); - public byte[] Value; + protected override NodeType Type => NodeType.LeafNode; + public LeafNode() { - nType = NodeType.LeafNode; } - public LeafNode(ReadOnlySpan val) : this() + public LeafNode(ReadOnlySpan val) { Value = val.ToArray(); } diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index 8fbcb22238..c80014633e 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -9,7 +9,7 @@ public abstract class MPTNode { private UInt256 hash; public bool Dirty { get; private set; } - protected NodeType nType; + protected abstract NodeType Type { get; } protected virtual UInt256 GenHash() { @@ -39,7 +39,7 @@ public byte[] Encode() using MemoryStream ms = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8, true); - writer.Write((byte)nType); + writer.Write((byte)Type); EncodeSpecific(writer); writer.Flush(); From 5b371050eedbd774544e62c807a14256d4c529c8 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 15:16:42 +0800 Subject: [PATCH 140/171] Use ReflectionCache --- src/neo/Cryptography/MPT/MPTNode.cs | 10 +++------- src/neo/Cryptography/MPT/MPTNodeType.cs | 6 ++++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index c80014633e..a4fabe7c5c 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -1,3 +1,4 @@ +using Neo.IO.Caching; using Neo.IO.Json; using System; using System.IO; @@ -57,13 +58,8 @@ public static unsafe MPTNode Decode(ReadOnlySpan data) using UnmanagedMemoryStream stream = new UnmanagedMemoryStream(pointer, data.Length); using BinaryReader reader = new BinaryReader(stream); - var n = (NodeType)reader.ReadByte() switch - { - NodeType.BranchNode => (MPTNode)new BranchNode(), - NodeType.ExtensionNode => new ExtensionNode(), - NodeType.LeafNode => new LeafNode(), - _ => throw new InvalidOperationException(), - }; + MPTNode n = (MPTNode)ReflectionCache.CreateInstance((NodeType)reader.ReadByte()); + if (n is null) throw new InvalidOperationException(); n.DecodeSpecific(reader); return n; diff --git a/src/neo/Cryptography/MPT/MPTNodeType.cs b/src/neo/Cryptography/MPT/MPTNodeType.cs index 657b9ba4d9..0194fa9664 100644 --- a/src/neo/Cryptography/MPT/MPTNodeType.cs +++ b/src/neo/Cryptography/MPT/MPTNodeType.cs @@ -1,10 +1,16 @@ +using Neo.IO.Caching; + namespace Neo.Cryptography.MPT { public enum NodeType : byte { + [ReflectionCache(typeof(BranchNode))] BranchNode = 0x00, + [ReflectionCache(typeof(ExtensionNode))] ExtensionNode = 0x01, + [ReflectionCache(typeof(HashNode))] HashNode = 0x02, + [ReflectionCache(typeof(LeafNode))] LeafNode = 0x03, } } From f744d7f83b0d36ccdb727b62b41ba549bc60278e Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 15:29:16 +0800 Subject: [PATCH 141/171] EmptyNode --- src/neo/Cryptography/MPT/BranchNode.cs | 2 +- src/neo/Cryptography/MPT/HashNode.cs | 12 ++++-------- src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs | 4 ++-- src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs | 2 +- src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs | 2 +- src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs | 2 +- src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 8 ++++---- src/neo/Cryptography/MPT/MPTTrie.Put.cs | 6 +++--- tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs | 6 ++---- 9 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs index b91a3e0f7e..964df8029a 100644 --- a/src/neo/Cryptography/MPT/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -14,7 +14,7 @@ public BranchNode() { for (int i = 0; i < ChildCount; i++) { - Children[i] = HashNode.EmptyNode(); + Children[i] = HashNode.EmptyNode; } } diff --git a/src/neo/Cryptography/MPT/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs index a13cf156c8..b606008309 100644 --- a/src/neo/Cryptography/MPT/HashNode.cs +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -10,6 +10,7 @@ public class HashNode : MPTNode public UInt256 Hash; protected override NodeType Type => NodeType.HashNode; + public static HashNode EmptyNode { get; } = new HashNode(); public HashNode() { @@ -25,16 +26,11 @@ protected override UInt256 GenHash() return Hash; } - public static HashNode EmptyNode() - { - return new HashNode(null); - } - - public bool IsEmptyNode => Hash is null; + public bool IsEmpty => Hash is null; public override void EncodeSpecific(BinaryWriter writer) { - if (this.IsEmptyNode) + if (this.IsEmpty) { writer.WriteVarBytes(Array.Empty()); return; @@ -57,7 +53,7 @@ public override void DecodeSpecific(BinaryReader reader) public override JObject ToJson() { var json = new JObject(); - if (!this.IsEmptyNode) + if (!this.IsEmpty) { json["hash"] = Hash.ToString(); } diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs index f963a21944..77872e018f 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs @@ -25,7 +25,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M } case HashNode hashNode: { - if (hashNode.IsEmptyNode) break; + if (hashNode.IsEmpty) break; var newNode = Resolve(hashNode); if (newNode is null) break; node = newNode; @@ -82,7 +82,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M } case HashNode hashNode: { - if (hashNode.IsEmptyNode) break; + if (hashNode.IsEmpty) break; var newNode = Resolve(hashNode); if (newNode is null) break; node = newNode; diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs index efa3fb4bab..5fdb564752 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs @@ -30,7 +30,7 @@ private bool TryGet(ref MPTNode node, ReadOnlySpan path, out ReadOnlySpan< } case HashNode hashNode: { - if (hashNode.IsEmptyNode) break; + if (hashNode.IsEmpty) break; var newNode = Resolve(hashNode); if (newNode is null) break; node = newNode; diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs index 41ab324cac..4fd18dffc6 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs @@ -32,7 +32,7 @@ private bool GetProof(ref MPTNode node, ReadOnlySpan path, HashSet } case HashNode hashNode: { - if (hashNode.IsEmptyNode) break; + if (hashNode.IsEmpty) break; var newNode = Resolve(hashNode); if (newNode is null) break; node = newNode; diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs index 32c4d67bf4..da9ee83e75 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs @@ -20,7 +20,7 @@ public MPTReadOnlyTrie(UInt256 root, IReadOnlyStore store, byte prefix) if (root is null) { - this.root = HashNode.EmptyNode(); + this.root = HashNode.EmptyNode; } else { diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index c27607e7e3..2ec01b3193 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -24,7 +24,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) { if (path.Length < 1) { - node = HashNode.EmptyNode(); + node = HashNode.EmptyNode; return true; } return false; @@ -35,7 +35,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) { var result = TryDelete(ref extensionNode.Next, path[extensionNode.Key.Length..]); if (!result) return false; - if (extensionNode.Next is HashNode hashNode && hashNode.IsEmptyNode) + if (extensionNode.Next is HashNode hashNode && hashNode.IsEmpty) { node = extensionNode.Next; return true; @@ -66,7 +66,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) List childrenIndexes = new List(); for (int i = 0; i < BranchNode.ChildCount; i++) { - if (branchNode.Children[i] is HashNode hn && hn.IsEmptyNode) continue; + if (branchNode.Children[i] is HashNode hn && hn.IsEmpty) continue; childrenIndexes.Add((byte)i); } if (childrenIndexes.Count > 1) @@ -105,7 +105,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) } case HashNode hashNode: { - if (hashNode.IsEmptyNode) + if (hashNode.IsEmpty) { return true; } diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs index 7eaca9cd31..d43bbd5047 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -74,8 +74,8 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) var pathRemain = path[prefix.Length..]; var keyRemain = extensionNode.Key.AsSpan(prefix.Length); var son = new BranchNode(); - MPTNode grandSon1 = HashNode.EmptyNode(); - MPTNode grandSon2 = HashNode.EmptyNode(); + MPTNode grandSon1 = HashNode.EmptyNode; + MPTNode grandSon2 = HashNode.EmptyNode; Put(ref grandSon1, keyRemain[1..], extensionNode.Next); son.Children[keyRemain[0]] = grandSon1; @@ -128,7 +128,7 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) case HashNode hashNode: { MPTNode newNode; - if (hashNode.IsEmptyNode) + if (hashNode.IsEmpty) { if (path.Length < 1) { diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index 2fe626e6c9..b1b9369377 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -107,8 +107,7 @@ public void TestInit() v2.Value = "2222".HexToBytes(); var v3 = new LeafNode(); v3.Value = Encoding.ASCII.GetBytes("hello"); - var h1 = new HashNode(); - h1.Hash = v3.GetHash(); + var h1 = new HashNode(v3.GetHash()); var l3 = new ExtensionNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); @@ -283,8 +282,7 @@ public void TestGetProof() v2.Value = "2222".HexToBytes(); var v3 = new LeafNode(); v3.Value = Encoding.ASCII.GetBytes("hello"); - var h1 = new HashNode(); - h1.Hash = v3.GetHash(); + var h1 = new HashNode(v3.GetHash()); var l3 = new ExtensionNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); From b3be508f039c02f8d20296300434fc6f557ee4d9 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 15:52:11 +0800 Subject: [PATCH 142/171] Remove MPTNode.Dirty --- src/neo/Cryptography/MPT/MPTNode.cs | 18 +++++------------- .../Cryptography/MPT/UT_MPTNode.cs | 7 ------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index a4fabe7c5c..afd774fccc 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -9,30 +9,22 @@ namespace Neo.Cryptography.MPT public abstract class MPTNode { private UInt256 hash; - public bool Dirty { get; private set; } + protected abstract NodeType Type { get; } protected virtual UInt256 GenHash() { - return new UInt256(Crypto.Hash256(this.Encode())); + return new UInt256(Crypto.Hash256(Encode())); } - public virtual UInt256 GetHash() + public UInt256 GetHash() { - if (!Dirty && !(hash is null)) return hash; - hash = GenHash(); - Dirty = false; - return hash; + return hash ??= GenHash(); } public void SetDirty() { - Dirty = true; - } - - public MPTNode() - { - Dirty = true; + hash = null; } public byte[] Encode() diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs index 6b15340896..eb06ad64e5 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs @@ -17,13 +17,6 @@ public void TestDecode() Assert.IsInstanceOfType(m, n.GetType()); } - [TestMethod] - public void TestFlag() - { - var n = new ExtensionNode(); - Assert.IsTrue(n.Dirty); - } - [TestMethod] public void TestHashNode() { From 6bea24d4d2ff325589d8e06813e61abf723d4acf Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 16:02:24 +0800 Subject: [PATCH 143/171] Remove GenHash() and GetHash() --- src/neo/Cryptography/MPT/BranchNode.cs | 2 +- src/neo/Cryptography/MPT/ExtensionNode.cs | 2 +- src/neo/Cryptography/MPT/HashNode.cs | 14 +++++--------- src/neo/Cryptography/MPT/MPTDb.cs | 2 +- src/neo/Cryptography/MPT/MPTNode.cs | 11 +---------- src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs | 2 +- tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs | 12 ++++++------ 7 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs index 964df8029a..d01f075052 100644 --- a/src/neo/Cryptography/MPT/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -22,7 +22,7 @@ public override void EncodeSpecific(BinaryWriter writer) { for (int i = 0; i < ChildCount; i++) { - var hashNode = new HashNode(Children[i].GetHash()); + var hashNode = new HashNode(Children[i].Hash); hashNode.EncodeSpecific(writer); } } diff --git a/src/neo/Cryptography/MPT/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs index 95424857d2..29fa28f695 100644 --- a/src/neo/Cryptography/MPT/ExtensionNode.cs +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -17,7 +17,7 @@ public class ExtensionNode : MPTNode public override void EncodeSpecific(BinaryWriter writer) { writer.WriteVarBytes(Key); - var hashNode = new HashNode(Next.GetHash()); + var hashNode = new HashNode(Next.Hash); hashNode.EncodeSpecific(writer); } diff --git a/src/neo/Cryptography/MPT/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs index b606008309..36c085c438 100644 --- a/src/neo/Cryptography/MPT/HashNode.cs +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -7,8 +7,9 @@ namespace Neo.Cryptography.MPT { public class HashNode : MPTNode { - public UInt256 Hash; + private UInt256 hash; + public override UInt256 Hash => hash; protected override NodeType Type => NodeType.HashNode; public static HashNode EmptyNode { get; } = new HashNode(); @@ -18,12 +19,7 @@ public HashNode() public HashNode(UInt256 hash) { - Hash = hash; - } - - protected override UInt256 GenHash() - { - return Hash; + this.hash = hash; } public bool IsEmpty => Hash is null; @@ -43,11 +39,11 @@ public override void DecodeSpecific(BinaryReader reader) var len = reader.ReadVarInt(); if (len < 1) { - Hash = null; + hash = null; return; } if (len != UInt256.Length) throw new InvalidOperationException("Invalid hash bytes"); - Hash = new UInt256(reader.ReadFixedBytes((int)len)); + hash = new UInt256(reader.ReadFixedBytes((int)len)); } public override JObject ToJson() diff --git a/src/neo/Cryptography/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs index 3d3f54c958..1175fbf953 100644 --- a/src/neo/Cryptography/MPT/MPTDb.cs +++ b/src/neo/Cryptography/MPT/MPTDb.cs @@ -16,7 +16,7 @@ public void Put(MPTNode node) { if (node is HashNode) throw new System.InvalidOperationException("Means nothing to store HashNode"); - store.Put(prefix, node.GetHash().ToArray(), node.Encode()); + store.Put(prefix, node.Hash.ToArray(), node.Encode()); } } } diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index afd774fccc..7f17246e66 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -10,18 +10,9 @@ public abstract class MPTNode { private UInt256 hash; + public virtual UInt256 Hash => hash ??= new UInt256(Crypto.Hash256(Encode())); protected abstract NodeType Type { get; } - protected virtual UInt256 GenHash() - { - return new UInt256(Crypto.Hash256(Encode())); - } - - public UInt256 GetHash() - { - return hash ??= GenHash(); - } - public void SetDirty() { hash = null; diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs index da9ee83e75..43008937fc 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs @@ -35,7 +35,7 @@ public MPTNode Resolve(HashNode n) public UInt256 GetRoot() { - return root.GetHash(); + return root.Hash; } protected static byte[] ToNibbles(ReadOnlySpan path) diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index b1b9369377..8b5c94a7f9 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -107,7 +107,7 @@ public void TestInit() v2.Value = "2222".HexToBytes(); var v3 = new LeafNode(); v3.Value = Encoding.ASCII.GetBytes("hello"); - var h1 = new HashNode(v3.GetHash()); + var h1 = new HashNode(v3.Hash); var l3 = new ExtensionNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); @@ -122,7 +122,7 @@ public void TestInit() var store = new MemoryStore(); var snapshot = store.GetSnapshot(); var db = new MPTDb(snapshot, 0); - this.rootHash = root.GetHash(); + this.rootHash = root.Hash; db.Put(r); db.Put(b); db.Put(l1); @@ -210,8 +210,8 @@ public void TestTryDelete() b.Children[9] = l2; r1.Next = v1; - Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.GetHash().ToString()); - Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.GetHash().ToString()); + Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.Hash.ToString()); + Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.Hash.ToString()); var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); var result = true; @@ -282,7 +282,7 @@ public void TestGetProof() v2.Value = "2222".HexToBytes(); var v3 = new LeafNode(); v3.Value = Encoding.ASCII.GetBytes("hello"); - var h1 = new HashNode(v3.GetHash()); + var h1 = new HashNode(v3.Hash); var l3 = new ExtensionNode(); l3.Next = h1; l3.Key = "0e".HexToBytes(); @@ -295,7 +295,7 @@ public void TestGetProof() b.Children[10] = l3; var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); - Assert.AreEqual(r.GetHash().ToString(), mpt.GetRoot().ToString()); + Assert.AreEqual(r.Hash.ToString(), mpt.GetRoot().ToString()); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); Assert.AreEqual(4, proof.Count); From 11599b353666c96663d9b4881e6cccfadab1069b Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 16:12:55 +0800 Subject: [PATCH 144/171] Optimize EncodeSpecific() --- src/neo/Cryptography/MPT/BranchNode.cs | 5 +---- src/neo/Cryptography/MPT/ExtensionNode.cs | 3 +-- src/neo/Cryptography/MPT/HashNode.cs | 7 +------ src/neo/Cryptography/MPT/MPTNode.cs | 9 +++++++++ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs index d01f075052..8e2a46cf83 100644 --- a/src/neo/Cryptography/MPT/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -21,10 +21,7 @@ public BranchNode() public override void EncodeSpecific(BinaryWriter writer) { for (int i = 0; i < ChildCount; i++) - { - var hashNode = new HashNode(Children[i].Hash); - hashNode.EncodeSpecific(writer); - } + WriteHash(writer, Children[i].Hash); } public override void DecodeSpecific(BinaryReader reader) diff --git a/src/neo/Cryptography/MPT/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs index 29fa28f695..091d29b590 100644 --- a/src/neo/Cryptography/MPT/ExtensionNode.cs +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -17,8 +17,7 @@ public class ExtensionNode : MPTNode public override void EncodeSpecific(BinaryWriter writer) { writer.WriteVarBytes(Key); - var hashNode = new HashNode(Next.Hash); - hashNode.EncodeSpecific(writer); + WriteHash(writer, Next.Hash); } public override void DecodeSpecific(BinaryReader reader) diff --git a/src/neo/Cryptography/MPT/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs index 36c085c438..ecd0e3b079 100644 --- a/src/neo/Cryptography/MPT/HashNode.cs +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -26,12 +26,7 @@ public HashNode(UInt256 hash) public override void EncodeSpecific(BinaryWriter writer) { - if (this.IsEmpty) - { - writer.WriteVarBytes(Array.Empty()); - return; - } - writer.WriteVarBytes(Hash.ToArray()); + WriteHash(writer, hash); } public override void DecodeSpecific(BinaryReader reader) diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index 7f17246e66..3624fcb078 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -1,3 +1,4 @@ +using Neo.IO; using Neo.IO.Caching; using Neo.IO.Json; using System; @@ -52,5 +53,13 @@ public static unsafe MPTNode Decode(ReadOnlySpan data) public abstract void DecodeSpecific(BinaryReader reader); public abstract JObject ToJson(); + + protected void WriteHash(BinaryWriter writer, UInt256 hash) + { + if (hash is null) + writer.Write((byte)0); + else + writer.WriteVarBytes(hash.ToArray()); + } } } From 7914efb2f95d245a4a6b9fea8466a0bda2bf16fe Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 16:23:21 +0800 Subject: [PATCH 145/171] Optimize DecodeSpecific() --- src/neo/Cryptography/MPT/BranchNode.cs | 5 ++--- src/neo/Cryptography/MPT/ExtensionNode.cs | 5 ++--- src/neo/Cryptography/MPT/HashNode.cs | 13 ++++++------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs index 8e2a46cf83..bf45245912 100644 --- a/src/neo/Cryptography/MPT/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -28,9 +28,8 @@ public override void DecodeSpecific(BinaryReader reader) { for (int i = 0; i < ChildCount; i++) { - var hashNode = new HashNode(); - hashNode.DecodeSpecific(reader); - Children[i] = hashNode; + Children[i] = new HashNode(); + Children[i].DecodeSpecific(reader); } } diff --git a/src/neo/Cryptography/MPT/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs index 091d29b590..57e091c6ab 100644 --- a/src/neo/Cryptography/MPT/ExtensionNode.cs +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -23,9 +23,8 @@ public override void EncodeSpecific(BinaryWriter writer) public override void DecodeSpecific(BinaryReader reader) { Key = reader.ReadVarBytes(MaxKeyLength); - var hashNode = new HashNode(); - hashNode.DecodeSpecific(reader); - Next = hashNode; + Next = new HashNode(); + Next.DecodeSpecific(reader); } public override JObject ToJson() diff --git a/src/neo/Cryptography/MPT/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs index ecd0e3b079..7d9a6545b4 100644 --- a/src/neo/Cryptography/MPT/HashNode.cs +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -31,14 +31,13 @@ public override void EncodeSpecific(BinaryWriter writer) public override void DecodeSpecific(BinaryReader reader) { - var len = reader.ReadVarInt(); - if (len < 1) + byte[] buffer = reader.ReadVarBytes(UInt256.Length); + hash = buffer.Length switch { - hash = null; - return; - } - if (len != UInt256.Length) throw new InvalidOperationException("Invalid hash bytes"); - hash = new UInt256(reader.ReadFixedBytes((int)len)); + 0 => null, + UInt256.Length => new UInt256(buffer), + _ => throw new FormatException() + }; } public override JObject ToJson() From 9e185be4dc3ce9879f6aa2f543d9da9377b00bd6 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 16:41:34 +0800 Subject: [PATCH 146/171] Optimize, simplify and format --- src/neo/Cryptography/MPT/BranchNode.cs | 12 ++++-------- src/neo/Cryptography/MPT/ExtensionNode.cs | 14 ++++++++------ src/neo/Cryptography/MPT/HashNode.cs | 7 +++---- src/neo/Cryptography/MPT/LeafNode.cs | 16 +++++++++------- src/neo/Cryptography/MPT/MPTDb.cs | 3 ++- src/neo/Cryptography/MPT/MPTNode.cs | 4 ++-- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs index bf45245912..a2afecee4f 100644 --- a/src/neo/Cryptography/MPT/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -1,5 +1,6 @@ using Neo.IO.Json; using System.IO; +using System.Linq; namespace Neo.Cryptography.MPT { @@ -18,13 +19,13 @@ public BranchNode() } } - public override void EncodeSpecific(BinaryWriter writer) + internal override void EncodeSpecific(BinaryWriter writer) { for (int i = 0; i < ChildCount; i++) WriteHash(writer, Children[i].Hash); } - public override void DecodeSpecific(BinaryReader reader) + internal override void DecodeSpecific(BinaryReader reader) { for (int i = 0; i < ChildCount; i++) { @@ -35,12 +36,7 @@ public override void DecodeSpecific(BinaryReader reader) public override JObject ToJson() { - var jarr = new JArray(); - for (int i = 0; i < ChildCount; i++) - { - jarr.Add(Children[i].ToJson()); - } - return jarr; + return new JArray(Children.Select(p => p.ToJson())); } } } diff --git a/src/neo/Cryptography/MPT/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs index 57e091c6ab..03a3bc8b61 100644 --- a/src/neo/Cryptography/MPT/ExtensionNode.cs +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -9,18 +9,19 @@ public class ExtensionNode : MPTNode { //max lenght when store StorageKey public const int MaxKeyLength = (InteropService.Storage.MaxKeySize + sizeof(int)) * 2; + public byte[] Key; public MPTNode Next; protected override NodeType Type => NodeType.ExtensionNode; - public override void EncodeSpecific(BinaryWriter writer) + internal override void EncodeSpecific(BinaryWriter writer) { writer.WriteVarBytes(Key); WriteHash(writer, Next.Hash); } - public override void DecodeSpecific(BinaryReader reader) + internal override void DecodeSpecific(BinaryReader reader) { Key = reader.ReadVarBytes(MaxKeyLength); Next = new HashNode(); @@ -29,10 +30,11 @@ public override void DecodeSpecific(BinaryReader reader) public override JObject ToJson() { - var json = new JObject(); - json["key"] = Key.ToHexString(); - json["next"] = Next.ToJson(); - return json; + return new JObject + { + ["key"] = Key.ToHexString(), + ["next"] = Next.ToJson() + }; } } } diff --git a/src/neo/Cryptography/MPT/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs index 7d9a6545b4..9d8eb9c41b 100644 --- a/src/neo/Cryptography/MPT/HashNode.cs +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -11,6 +11,7 @@ public class HashNode : MPTNode public override UInt256 Hash => hash; protected override NodeType Type => NodeType.HashNode; + public bool IsEmpty => Hash is null; public static HashNode EmptyNode { get; } = new HashNode(); public HashNode() @@ -22,14 +23,12 @@ public HashNode(UInt256 hash) this.hash = hash; } - public bool IsEmpty => Hash is null; - - public override void EncodeSpecific(BinaryWriter writer) + internal override void EncodeSpecific(BinaryWriter writer) { WriteHash(writer, hash); } - public override void DecodeSpecific(BinaryReader reader) + internal override void DecodeSpecific(BinaryReader reader) { byte[] buffer = reader.ReadVarBytes(UInt256.Length); hash = buffer.Length switch diff --git a/src/neo/Cryptography/MPT/LeafNode.cs b/src/neo/Cryptography/MPT/LeafNode.cs index b02c71f793..ebc7248774 100644 --- a/src/neo/Cryptography/MPT/LeafNode.cs +++ b/src/neo/Cryptography/MPT/LeafNode.cs @@ -10,6 +10,7 @@ public class LeafNode : MPTNode { //the max size when store StorageItem public const int MaxValueLength = 3 + InteropService.Storage.MaxValueSize + sizeof(bool); + public byte[] Value; protected override NodeType Type => NodeType.LeafNode; @@ -18,26 +19,27 @@ public LeafNode() { } - public LeafNode(ReadOnlySpan val) + public LeafNode(ReadOnlySpan value) { - Value = val.ToArray(); + Value = value.ToArray(); } - public override void EncodeSpecific(BinaryWriter writer) + internal override void EncodeSpecific(BinaryWriter writer) { writer.WriteVarBytes(Value); } - public override void DecodeSpecific(BinaryReader reader) + internal override void DecodeSpecific(BinaryReader reader) { Value = reader.ReadVarBytes(MaxValueLength); } public override JObject ToJson() { - var json = new JObject(); - json["value"] = Value.ToHexString(); - return json; + return new JObject + { + ["value"] = Value.ToHexString() + }; } } } diff --git a/src/neo/Cryptography/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs index 1175fbf953..b14e2e0b45 100644 --- a/src/neo/Cryptography/MPT/MPTDb.cs +++ b/src/neo/Cryptography/MPT/MPTDb.cs @@ -1,5 +1,6 @@ using Neo.IO; using Neo.Persistence; +using System; namespace Neo.Cryptography.MPT { @@ -15,7 +16,7 @@ public MPTDb(ISnapshot store, byte prefix) : base(store, prefix) public void Put(MPTNode node) { if (node is HashNode) - throw new System.InvalidOperationException("Means nothing to store HashNode"); + throw new InvalidOperationException("Means nothing to store HashNode"); store.Put(prefix, node.Hash.ToArray(), node.Encode()); } } diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index 3624fcb078..622ba780c8 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -31,7 +31,7 @@ public byte[] Encode() return ms.ToArray(); } - public abstract void EncodeSpecific(BinaryWriter writer); + internal abstract void EncodeSpecific(BinaryWriter writer); public static unsafe MPTNode Decode(ReadOnlySpan data) { @@ -50,7 +50,7 @@ public static unsafe MPTNode Decode(ReadOnlySpan data) } } - public abstract void DecodeSpecific(BinaryReader reader); + internal abstract void DecodeSpecific(BinaryReader reader); public abstract JObject ToJson(); From 3e5646bb1fcd144f87a7ce0e510d58ce918e1899 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 16:45:01 +0800 Subject: [PATCH 147/171] Format --- src/neo/Cryptography/MPT/MPTDb.cs | 2 +- src/neo/Cryptography/MPT/MPTReadOnlyDb.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs index b14e2e0b45..993543b193 100644 --- a/src/neo/Cryptography/MPT/MPTDb.cs +++ b/src/neo/Cryptography/MPT/MPTDb.cs @@ -17,7 +17,7 @@ public void Put(MPTNode node) { if (node is HashNode) throw new InvalidOperationException("Means nothing to store HashNode"); - store.Put(prefix, node.Hash.ToArray(), node.Encode()); + store.Put(Prefix, node.Hash.ToArray(), node.Encode()); } } } diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs b/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs index 7a91745e4e..aa0a92bda1 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs @@ -6,18 +6,18 @@ namespace Neo.Cryptography.MPT public class MPTReadOnlyDb { private readonly IReadOnlyStore store; - protected readonly byte prefix; + protected readonly byte Prefix; public MPTReadOnlyDb(IReadOnlyStore store, byte prefix) { this.store = store; - this.prefix = prefix; + this.Prefix = prefix; } public MPTNode Node(UInt256 hash) { if (hash is null) return null; - var data = store.TryGet(prefix, hash.ToArray()); + var data = store.TryGet(Prefix, hash.ToArray()); return MPTNode.Decode(data); } } From 39274d394636bb49fc7c303a22b4e920cd11ec54 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 16:50:29 +0800 Subject: [PATCH 148/171] Update MPTNode.cs --- src/neo/Cryptography/MPT/MPTNode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index 622ba780c8..9cd54471e9 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -3,7 +3,6 @@ using Neo.IO.Json; using System; using System.IO; -using System.Text; namespace Neo.Cryptography.MPT { @@ -22,7 +21,7 @@ public void SetDirty() public byte[] Encode() { using MemoryStream ms = new MemoryStream(); - using BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8, true); + using BinaryWriter writer = new BinaryWriter(ms); writer.Write((byte)Type); EncodeSpecific(writer); From 9ea1ff7581430dca371cbb45b147b206946d5c30 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 18 May 2020 15:18:36 +0800 Subject: [PATCH 149/171] use MemoryStore replace ProofStore --- src/neo/Cryptography/MPT/MPTProofStore.cs | 37 ------------------- .../Cryptography/MPT/MPTReadOnlyTrie.Proof.cs | 9 ++++- 2 files changed, 7 insertions(+), 39 deletions(-) delete mode 100644 src/neo/Cryptography/MPT/MPTProofStore.cs diff --git a/src/neo/Cryptography/MPT/MPTProofStore.cs b/src/neo/Cryptography/MPT/MPTProofStore.cs deleted file mode 100644 index 9805b8e476..0000000000 --- a/src/neo/Cryptography/MPT/MPTProofStore.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Neo.IO; -using Neo.Persistence; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Neo.Cryptography.MPT -{ - public class MPTProofStore : IReadOnlyStore - { - private readonly Dictionary store = new Dictionary(ByteArrayEqualityComparer.Default); - - public MPTProofStore(IEnumerable proof) - { - foreach (byte[] data in proof) - { - store.Add(Crypto.Hash256(data), data); - } - } - - public byte[] TryGet(byte prefix, byte[] hash) - { - var result = store.TryGetValue(hash, out byte[] value); - return result ? value : null; - } - - public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) - { - IEnumerable> records = store; - if (prefix?.Length > 0) - records = records.Where(p => p.Key.AsSpan().StartsWith(prefix)); - records = records.OrderBy(p => p.Key, ByteArrayComparer.Default); - foreach (var pair in records) - yield return (pair.Key, pair.Value); - } - } -} diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs index 4fd18dffc6..0dd0662c2a 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs @@ -62,8 +62,13 @@ private bool GetProof(ref MPTNode node, ReadOnlySpan path, HashSet public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) { - var store = new MPTProofStore(proof); - var trie = new MPTReadOnlyTrie(root, store, 0); + byte table = 0; + var memoryStore = new MemoryStore(); + var trie = new MPTReadOnlyTrie(root, memoryStore, table); + foreach (byte[] data in proof) + { + memoryStore.Put(table, Crypto.Hash256(data), data); + } return trie.Get(key); } } From 03ca94d75367d5e1fe6e3bba1597b363c7d275ea Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 18 May 2020 15:53:49 +0800 Subject: [PATCH 150/171] rm ToJson() --- src/neo/Cryptography/MPT/BranchNode.cs | 5 ----- src/neo/Cryptography/MPT/ExtensionNode.cs | 9 --------- src/neo/Cryptography/MPT/HashNode.cs | 10 ---------- src/neo/Cryptography/MPT/LeafNode.cs | 8 -------- src/neo/Cryptography/MPT/MPTNode.cs | 2 -- src/neo/Cryptography/MPT/MPTTrie.cs | 5 ----- tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs | 3 +-- 7 files changed, 1 insertion(+), 41 deletions(-) diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs index a2afecee4f..5622cd6f2c 100644 --- a/src/neo/Cryptography/MPT/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -33,10 +33,5 @@ internal override void DecodeSpecific(BinaryReader reader) Children[i].DecodeSpecific(reader); } } - - public override JObject ToJson() - { - return new JArray(Children.Select(p => p.ToJson())); - } } } diff --git a/src/neo/Cryptography/MPT/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs index 03a3bc8b61..322eb29b70 100644 --- a/src/neo/Cryptography/MPT/ExtensionNode.cs +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -27,14 +27,5 @@ internal override void DecodeSpecific(BinaryReader reader) Next = new HashNode(); Next.DecodeSpecific(reader); } - - public override JObject ToJson() - { - return new JObject - { - ["key"] = Key.ToHexString(), - ["next"] = Next.ToJson() - }; - } } } diff --git a/src/neo/Cryptography/MPT/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs index 9d8eb9c41b..d4b034937a 100644 --- a/src/neo/Cryptography/MPT/HashNode.cs +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -38,15 +38,5 @@ internal override void DecodeSpecific(BinaryReader reader) _ => throw new FormatException() }; } - - public override JObject ToJson() - { - var json = new JObject(); - if (!this.IsEmpty) - { - json["hash"] = Hash.ToString(); - } - return json; - } } } diff --git a/src/neo/Cryptography/MPT/LeafNode.cs b/src/neo/Cryptography/MPT/LeafNode.cs index ebc7248774..651bbcd409 100644 --- a/src/neo/Cryptography/MPT/LeafNode.cs +++ b/src/neo/Cryptography/MPT/LeafNode.cs @@ -33,13 +33,5 @@ internal override void DecodeSpecific(BinaryReader reader) { Value = reader.ReadVarBytes(MaxValueLength); } - - public override JObject ToJson() - { - return new JObject - { - ["value"] = Value.ToHexString() - }; - } } } diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index 9cd54471e9..a98f7b9a4d 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -51,8 +51,6 @@ public static unsafe MPTNode Decode(ReadOnlySpan data) internal abstract void DecodeSpecific(BinaryReader reader); - public abstract JObject ToJson(); - protected void WriteHash(BinaryWriter writer, UInt256 hash) { if (hash is null) diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index f92af2ce70..fdaa0b5695 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -14,10 +14,5 @@ public MPTTrie(UInt256 root, ISnapshot store, byte prefix) : base(root, store, p { this.db = new MPTDb(store, prefix); } - - public JObject ToJson() - { - return root.ToJson(); - } } } diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index 8b5c94a7f9..4796729e43 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -262,8 +262,7 @@ public void TestBranchNodeRemainValue() result = mpt.Delete("ac11".HexToBytes()); Assert.IsTrue(result); result = mpt.Delete("ac22".HexToBytes()); - Assert.IsTrue(result); - Assert.AreEqual("{\"key\":\"0a0c\",\"next\":{\"value\":\"ac\"}}", mpt.ToJson().ToString()); + Assert.IsFalse(mpt.Get("ac".HexToBytes()) is null); } [TestMethod] From f662cb0ac0fc57b85db7cefbd43caef48a96c217 Mon Sep 17 00:00:00 2001 From: zhangtao Date: Mon, 18 May 2020 22:01:31 +0800 Subject: [PATCH 151/171] remove ReadOnlyTrie --- src/neo/Cryptography/MPT/MPTDb.cs | 19 +++++-- src/neo/Cryptography/MPT/MPTReadOnlyDb.cs | 24 --------- src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs | 52 ------------------- src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 2 +- ...PTReadOnlyTrie.Find.cs => MPTTrie.Find.cs} | 2 +- ...{MPTReadOnlyTrie.Get.cs => MPTTrie.Get.cs} | 2 +- ...ReadOnlyTrie.Proof.cs => MPTTrie.Proof.cs} | 9 +++- src/neo/Cryptography/MPT/MPTTrie.Put.cs | 2 +- src/neo/Cryptography/MPT/MPTTrie.cs | 40 ++++++++++++-- 9 files changed, 62 insertions(+), 90 deletions(-) delete mode 100644 src/neo/Cryptography/MPT/MPTReadOnlyDb.cs delete mode 100644 src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs rename src/neo/Cryptography/MPT/{MPTReadOnlyTrie.Find.cs => MPTTrie.Find.cs} (98%) rename src/neo/Cryptography/MPT/{MPTReadOnlyTrie.Get.cs => MPTTrie.Get.cs} (97%) rename src/neo/Cryptography/MPT/{MPTReadOnlyTrie.Proof.cs => MPTTrie.Proof.cs} (89%) diff --git a/src/neo/Cryptography/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs index 993543b193..9c46702f01 100644 --- a/src/neo/Cryptography/MPT/MPTDb.cs +++ b/src/neo/Cryptography/MPT/MPTDb.cs @@ -4,20 +4,29 @@ namespace Neo.Cryptography.MPT { - public class MPTDb : MPTReadOnlyDb + public class MPTDb { - private readonly ISnapshot store; + private readonly ISnapshot Store; + private readonly byte Prefix; - public MPTDb(ISnapshot store, byte prefix) : base(store, prefix) + public MPTDb(ISnapshot store, byte prefix) { - this.store = store; + this.Store = store; + this.Prefix = prefix; + } + + public MPTNode Node(UInt256 hash) + { + if (hash is null) return null; + var data = Store.TryGet(Prefix, hash.ToArray()); + return MPTNode.Decode(data); } public void Put(MPTNode node) { if (node is HashNode) throw new InvalidOperationException("Means nothing to store HashNode"); - store.Put(Prefix, node.Hash.ToArray(), node.Encode()); + Store.Put(Prefix, node.Hash.ToArray(), node.Encode()); } } } diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs b/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs deleted file mode 100644 index aa0a92bda1..0000000000 --- a/src/neo/Cryptography/MPT/MPTReadOnlyDb.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Neo.IO; -using Neo.Persistence; - -namespace Neo.Cryptography.MPT -{ - public class MPTReadOnlyDb - { - private readonly IReadOnlyStore store; - protected readonly byte Prefix; - - public MPTReadOnlyDb(IReadOnlyStore store, byte prefix) - { - this.store = store; - this.Prefix = prefix; - } - - public MPTNode Node(UInt256 hash) - { - if (hash is null) return null; - var data = store.TryGet(Prefix, hash.ToArray()); - return MPTNode.Decode(data); - } - } -} diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs b/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs deleted file mode 100644 index 43008937fc..0000000000 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Neo.IO; -using Neo.Persistence; -using System; - -namespace Neo.Cryptography.MPT -{ - public partial class MPTReadOnlyTrie - where TKey : notnull, ISerializable, new() - where TValue : class, ISerializable, new() - { - private readonly MPTReadOnlyDb rodb; - protected MPTNode root; - - public MPTReadOnlyTrie(UInt256 root, IReadOnlyStore store, byte prefix) - { - if (store is null) - throw new ArgumentNullException(); - - this.rodb = new MPTReadOnlyDb(store, prefix); - - if (root is null) - { - this.root = HashNode.EmptyNode; - } - else - { - this.root = new HashNode(root); - } - } - - public MPTNode Resolve(HashNode n) - { - return rodb.Node(n.Hash); - } - - public UInt256 GetRoot() - { - return root.Hash; - } - - protected static byte[] ToNibbles(ReadOnlySpan path) - { - var result = new byte[path.Length * 2]; - for (int i = 0; i < path.Length; i++) - { - result[i * 2] = (byte)(path[i] >> 4); - result[i * 2 + 1] = (byte)(path[i] & 0x0F); - } - return result; - } - } -} diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index 2ec01b3193..d9b10de8a2 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -5,7 +5,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTTrie : MPTReadOnlyTrie + public partial class MPTTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs b/src/neo/Cryptography/MPT/MPTTrie.Find.cs similarity index 98% rename from src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs rename to src/neo/Cryptography/MPT/MPTTrie.Find.cs index 77872e018f..cfa92dbbce 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Find.cs @@ -6,7 +6,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTReadOnlyTrie + public partial class MPTTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs b/src/neo/Cryptography/MPT/MPTTrie.Get.cs similarity index 97% rename from src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs rename to src/neo/Cryptography/MPT/MPTTrie.Get.cs index 5fdb564752..7340815ed5 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Get.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Get.cs @@ -3,7 +3,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTReadOnlyTrie + public partial class MPTTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { diff --git a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs similarity index 89% rename from src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs rename to src/neo/Cryptography/MPT/MPTTrie.Proof.cs index 0dd0662c2a..53dc979072 100644 --- a/src/neo/Cryptography/MPT/MPTReadOnlyTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -5,7 +5,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTReadOnlyTrie + public partial class MPTTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { @@ -64,11 +64,16 @@ public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) { byte table = 0; var memoryStore = new MemoryStore(); - var trie = new MPTReadOnlyTrie(root, memoryStore, table); foreach (byte[] data in proof) { memoryStore.Put(table, Crypto.Hash256(data), data); } + ISnapshot snapshot = memoryStore.GetSnapshot(); + var trie = new MPTTrie(root, snapshot, table); + foreach (byte[] data in proof) + { + snapshot.Put(table, Crypto.Hash256(data), data); + } return trie.Get(key); } } diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs index d43bbd5047..702f9f6f52 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -3,7 +3,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTTrie : MPTReadOnlyTrie + public partial class MPTTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index fdaa0b5695..7a65523bba 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -1,18 +1,52 @@ using Neo.IO; -using Neo.IO.Json; using Neo.Persistence; +using System; namespace Neo.Cryptography.MPT { - public partial class MPTTrie : MPTReadOnlyTrie + public partial class MPTTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { private readonly MPTDb db; + private MPTNode root; - public MPTTrie(UInt256 root, ISnapshot store, byte prefix) : base(root, store, prefix) + public MPTTrie(UInt256 root, ISnapshot store, byte prefix) { + if (store is null) + throw new ArgumentNullException(); + this.db = new MPTDb(store, prefix); + + if (root is null) + { + this.root = HashNode.EmptyNode; + } + else + { + this.root = new HashNode(root); + } + } + + public MPTNode Resolve(HashNode n) + { + return db.Node(n.Hash); + } + + public UInt256 GetRoot() + { + return root.Hash; + } + + protected static byte[] ToNibbles(ReadOnlySpan path) + { + var result = new byte[path.Length * 2]; + for (int i = 0; i < path.Length; i++) + { + result[i * 2] = (byte)(path[i] >> 4); + result[i * 2 + 1] = (byte)(path[i] & 0x0F); + } + return result; } } } From 6895937f8821be610f36d53a65047736558f3deb Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 15:33:30 +0800 Subject: [PATCH 152/171] Use constant prefix --- src/neo/Cryptography/MPT/MPTDb.cs | 6 ++-- src/neo/Cryptography/MPT/MPTTrie.Proof.cs | 7 ++--- src/neo/Cryptography/MPT/MPTTrie.cs | 4 +-- .../Cryptography/MPT/UT_MPTTrie.cs | 30 +++++++++---------- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs index 9c46702f01..2eb98dcc13 100644 --- a/src/neo/Cryptography/MPT/MPTDb.cs +++ b/src/neo/Cryptography/MPT/MPTDb.cs @@ -6,13 +6,13 @@ namespace Neo.Cryptography.MPT { public class MPTDb { + internal const byte Prefix = 0xf0; + private readonly ISnapshot Store; - private readonly byte Prefix; - public MPTDb(ISnapshot store, byte prefix) + public MPTDb(ISnapshot store) { this.Store = store; - this.Prefix = prefix; } public MPTNode Node(UInt256 hash) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs index 53dc979072..cef65089fb 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -62,17 +62,16 @@ private bool GetProof(ref MPTNode node, ReadOnlySpan path, HashSet public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) { - byte table = 0; var memoryStore = new MemoryStore(); foreach (byte[] data in proof) { - memoryStore.Put(table, Crypto.Hash256(data), data); + memoryStore.Put(MPTDb.Prefix, Crypto.Hash256(data), data); } ISnapshot snapshot = memoryStore.GetSnapshot(); - var trie = new MPTTrie(root, snapshot, table); + var trie = new MPTTrie(root, snapshot); foreach (byte[] data in proof) { - snapshot.Put(table, Crypto.Hash256(data), data); + snapshot.Put(MPTDb.Prefix, Crypto.Hash256(data), data); } return trie.Get(key); } diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index 7a65523bba..0646f89a14 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -11,12 +11,12 @@ public partial class MPTTrie private readonly MPTDb db; private MPTNode root; - public MPTTrie(UInt256 root, ISnapshot store, byte prefix) + public MPTTrie(UInt256 root, ISnapshot store) { if (store is null) throw new ArgumentNullException(); - this.db = new MPTDb(store, prefix); + this.db = new MPTDb(store); if (root is null) { diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index 4796729e43..c8d5ad33dc 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -121,7 +121,7 @@ public void TestInit() root = r; var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var db = new MPTDb(snapshot, 0); + var db = new MPTDb(snapshot); this.rootHash = root.Hash; db.Put(r); db.Put(b); @@ -138,7 +138,7 @@ public void TestInit() [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); TestValue value = mpt.Get("ac01".HexToBytes()); Assert.IsNotNull(value); Assert.AreEqual("abcd", value.ToString()); @@ -163,7 +163,7 @@ public void TestTryGet() [TestMethod] public void TestTryGetResolve() { - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); TestValue value = mpt.Get("acae".HexToBytes()); Assert.IsNotNull(value); Assert.AreEqual(Encoding.ASCII.GetBytes("hello").ToHexString(), value.ToString()); @@ -173,7 +173,7 @@ public void TestTryGetResolve() public void TestTryPut() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store.GetSnapshot(), 0); + var mpt = new MPTTrie(null, store.GetSnapshot()); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); @@ -213,7 +213,7 @@ public void TestTryDelete() Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.Hash.ToString()); Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.Hash.ToString()); - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); var result = true; TestValue value = mpt.Get("ac99".HexToBytes()); Assert.IsNotNull(value); @@ -229,7 +229,7 @@ public void TestDeleteSameValue() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt = new MPTTrie(null, snapshot, 0); + var mpt = new MPTTrie(null, snapshot); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes()); @@ -243,7 +243,7 @@ public void TestDeleteSameValue() Assert.IsNotNull(value); snapshot.Commit(); - var mpt0 = new MPTTrie(mpt.GetRoot(), store.GetSnapshot(), 0); + var mpt0 = new MPTTrie(mpt.GetRoot(), store.GetSnapshot()); value = mpt0.Get("ac02".HexToBytes()); Assert.IsNotNull(value); } @@ -252,7 +252,7 @@ public void TestDeleteSameValue() public void TestBranchNodeRemainValue() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store.GetSnapshot(), 0); + var mpt = new MPTTrie(null, store.GetSnapshot()); var result = mpt.Put("ac11".HexToBytes(), "ac11".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac22".HexToBytes(), "ac22".HexToBytes()); @@ -293,7 +293,7 @@ public void TestGetProof() l2.Next = v2; b.Children[10] = l3; - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); Assert.AreEqual(r.Hash.ToString(), mpt.GetRoot().ToString()); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); @@ -307,7 +307,7 @@ public void TestGetProof() [TestMethod] public void TestVerifyProof() { - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot(), 0); + var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); TestValue value = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof); @@ -320,7 +320,7 @@ public void TestAddLongerKey() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt = new MPTTrie(null, snapshot, 0); + var mpt = new MPTTrie(null, snapshot); var result = mpt.Put(new byte[] { 0xab }, new byte[] { 0x01 }); Assert.IsTrue(result); result = mpt.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x02 }); @@ -332,12 +332,12 @@ public void TestSplitKey() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt1 = new MPTTrie(null, snapshot, 0); + var mpt1 = new MPTTrie(null, snapshot); Assert.IsTrue(mpt1.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); Assert.IsTrue(mpt1.Put(new byte[] { 0xab }, new byte[] { 0x02 })); Assert.IsTrue(mpt1.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set1)); Assert.AreEqual(4, set1.Count); - var mpt2 = new MPTTrie(null, snapshot, 0); + var mpt2 = new MPTTrie(null, snapshot); Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x02 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); Assert.IsTrue(mpt2.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set2)); @@ -350,10 +350,10 @@ public void TestFind() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt1 = new MPTTrie(null, snapshot, 0); + var mpt1 = new MPTTrie(null, snapshot); var results = mpt1.Find(ReadOnlySpan.Empty).ToArray(); Assert.AreEqual(0, results.Count()); - var mpt2 = new MPTTrie(null, snapshot, 0); + var mpt2 = new MPTTrie(null, snapshot); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xef }, new byte[] { 0x01 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xe1 }, new byte[] { 0x02 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x03 })); From a223dbd60641f1c9b2ea8988e61c0107c50abbf7 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 15:40:39 +0800 Subject: [PATCH 153/171] Rename --- src/neo/Cryptography/MPT/MPTDb.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs index 2eb98dcc13..c7b7db3819 100644 --- a/src/neo/Cryptography/MPT/MPTDb.cs +++ b/src/neo/Cryptography/MPT/MPTDb.cs @@ -8,17 +8,17 @@ public class MPTDb { internal const byte Prefix = 0xf0; - private readonly ISnapshot Store; + private readonly ISnapshot store; public MPTDb(ISnapshot store) { - this.Store = store; + this.store = store; } public MPTNode Node(UInt256 hash) { if (hash is null) return null; - var data = Store.TryGet(Prefix, hash.ToArray()); + var data = store.TryGet(Prefix, hash.ToArray()); return MPTNode.Decode(data); } @@ -26,7 +26,7 @@ public void Put(MPTNode node) { if (node is HashNode) throw new InvalidOperationException("Means nothing to store HashNode"); - Store.Put(Prefix, node.Hash.ToArray(), node.Encode()); + store.Put(Prefix, node.Hash.ToArray(), node.Encode()); } } } From 0d91319be2caa00d6d2765878ee15c2587295eb1 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 16:08:30 +0800 Subject: [PATCH 154/171] Remove MPTDb --- src/neo/Cryptography/MPT/MPTDb.cs | 32 ----------- src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 8 +-- src/neo/Cryptography/MPT/MPTTrie.Proof.cs | 6 +- src/neo/Cryptography/MPT/MPTTrie.Put.cs | 21 ++++--- src/neo/Cryptography/MPT/MPTTrie.cs | 24 +++----- .../Cryptography/MPT/UT_MPTTrie.cs | 55 ++++++++++--------- 6 files changed, 56 insertions(+), 90 deletions(-) delete mode 100644 src/neo/Cryptography/MPT/MPTDb.cs diff --git a/src/neo/Cryptography/MPT/MPTDb.cs b/src/neo/Cryptography/MPT/MPTDb.cs deleted file mode 100644 index c7b7db3819..0000000000 --- a/src/neo/Cryptography/MPT/MPTDb.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Neo.IO; -using Neo.Persistence; -using System; - -namespace Neo.Cryptography.MPT -{ - public class MPTDb - { - internal const byte Prefix = 0xf0; - - private readonly ISnapshot store; - - public MPTDb(ISnapshot store) - { - this.store = store; - } - - public MPTNode Node(UInt256 hash) - { - if (hash is null) return null; - var data = store.TryGet(Prefix, hash.ToArray()); - return MPTNode.Decode(data); - } - - public void Put(MPTNode node) - { - if (node is HashNode) - throw new InvalidOperationException("Means nothing to store HashNode"); - store.Put(Prefix, node.Hash.ToArray(), node.Encode()); - } - } -} diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index d9b10de8a2..f43cbe9a1e 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -46,7 +46,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) extensionNode.Next = sn.Next; } extensionNode.SetDirty(); - db.Put(extensionNode); + PutToStore(extensionNode); return true; } return false; @@ -72,7 +72,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) if (childrenIndexes.Count > 1) { branchNode.SetDirty(); - db.Put(branchNode); + PutToStore(branchNode); return true; } var lastChildIndex = childrenIndexes[0]; @@ -91,7 +91,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) { exNode.Key = Concat(childrenIndexes.ToArray(), exNode.Key); exNode.SetDirty(); - db.Put(exNode); + PutToStore(exNode); node = exNode; return true; } @@ -100,7 +100,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) Key = childrenIndexes.ToArray(), Next = lastChild, }; - db.Put(node); + PutToStore(node); return true; } case HashNode hashNode: diff --git a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs index cef65089fb..3e76a5d295 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -65,13 +65,13 @@ public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) var memoryStore = new MemoryStore(); foreach (byte[] data in proof) { - memoryStore.Put(MPTDb.Prefix, Crypto.Hash256(data), data); + memoryStore.Put(Prefix, Crypto.Hash256(data), data); } ISnapshot snapshot = memoryStore.GetSnapshot(); - var trie = new MPTTrie(root, snapshot); + var trie = new MPTTrie(snapshot, root); foreach (byte[] data in proof) { - snapshot.Put(MPTDb.Prefix, Crypto.Hash256(data), data); + snapshot.Put(Prefix, Crypto.Hash256(data), data); } return trie.Get(key); } diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs index 702f9f6f52..78c0b657b4 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -46,13 +46,13 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) if (path.Length < 1) { node = v; - db.Put(node); + PutToStore(node); return true; } var branch = new BranchNode(); branch.Children[BranchNode.ChildCount - 1] = leafNode; Put(ref branch.Children[path[0]], path[1..], v); - db.Put(branch); + PutToStore(branch); node = branch; return true; } @@ -66,7 +66,7 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) if (result) { extensionNode.SetDirty(); - db.Put(extensionNode); + PutToStore(extensionNode); } return result; } @@ -90,7 +90,7 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) Put(ref grandSon2, pathRemain[1..], val); son.Children[pathRemain[0]] = grandSon2; } - db.Put(son); + PutToStore(son); if (prefix.Length > 0) { var exNode = new ExtensionNode() @@ -98,7 +98,7 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) Key = prefix.ToArray(), Next = son, }; - db.Put(exNode); + PutToStore(exNode); node = exNode; } else @@ -121,7 +121,7 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) if (result) { branchNode.SetDirty(); - db.Put(branchNode); + PutToStore(branchNode); } return result; } @@ -141,10 +141,10 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) Key = path.ToArray(), Next = val, }; - db.Put(newNode); + PutToStore(newNode); } node = newNode; - if (val is LeafNode) db.Put(val); + if (val is LeafNode) PutToStore(val); return true; } newNode = Resolve(hashNode); @@ -156,5 +156,10 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) return false; } } + + private void PutToStore(MPTNode node) + { + store.Put(Prefix, node.Hash.ToArray(), node.Encode()); + } } } diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index 0646f89a14..bcb3fc7e5b 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -8,29 +8,21 @@ public partial class MPTTrie where TKey : notnull, ISerializable, new() where TValue : class, ISerializable, new() { - private readonly MPTDb db; + private const byte Prefix = 0xf0; + + private readonly ISnapshot store; private MPTNode root; - public MPTTrie(UInt256 root, ISnapshot store) + public MPTTrie(ISnapshot store, UInt256 root) { - if (store is null) - throw new ArgumentNullException(); - - this.db = new MPTDb(store); - - if (root is null) - { - this.root = HashNode.EmptyNode; - } - else - { - this.root = new HashNode(root); - } + this.store = store ?? throw new ArgumentNullException(); + this.root = root is null ? HashNode.EmptyNode : new HashNode(root); } public MPTNode Resolve(HashNode n) { - return db.Node(n.Hash); + var data = store.TryGet(Prefix, n.Hash.ToArray()); + return MPTNode.Decode(data); } public UInt256 GetRoot() diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index c8d5ad33dc..917c1bc4c4 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -91,6 +91,11 @@ public class UT_MPTTrie private UInt256 rootHash; + private void PutToStore(MPTNode node) + { + mptdb.Put(0xf0, node.Hash.ToArray(), node.Encode()); + } + [TestInitialize] public void TestInit() { @@ -119,26 +124,22 @@ public void TestInit() l2.Next = v2; b.Children[10] = l3; root = r; - var store = new MemoryStore(); - var snapshot = store.GetSnapshot(); - var db = new MPTDb(snapshot); + this.mptdb = new MemoryStore(); this.rootHash = root.Hash; - db.Put(r); - db.Put(b); - db.Put(l1); - db.Put(l2); - db.Put(l3); - db.Put(v1); - db.Put(v2); - db.Put(v3); - snapshot.Commit(); - this.mptdb = store; + PutToStore(r); + PutToStore(b); + PutToStore(l1); + PutToStore(l2); + PutToStore(l3); + PutToStore(v1); + PutToStore(v2); + PutToStore(v3); } [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); + var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); TestValue value = mpt.Get("ac01".HexToBytes()); Assert.IsNotNull(value); Assert.AreEqual("abcd", value.ToString()); @@ -163,7 +164,7 @@ public void TestTryGet() [TestMethod] public void TestTryGetResolve() { - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); + var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); TestValue value = mpt.Get("acae".HexToBytes()); Assert.IsNotNull(value); Assert.AreEqual(Encoding.ASCII.GetBytes("hello").ToHexString(), value.ToString()); @@ -173,7 +174,7 @@ public void TestTryGetResolve() public void TestTryPut() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store.GetSnapshot()); + var mpt = new MPTTrie(store.GetSnapshot(), null); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); @@ -213,7 +214,7 @@ public void TestTryDelete() Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.Hash.ToString()); Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.Hash.ToString()); - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); + var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); var result = true; TestValue value = mpt.Get("ac99".HexToBytes()); Assert.IsNotNull(value); @@ -229,7 +230,7 @@ public void TestDeleteSameValue() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt = new MPTTrie(null, snapshot); + var mpt = new MPTTrie(snapshot, null); var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes()); @@ -243,7 +244,7 @@ public void TestDeleteSameValue() Assert.IsNotNull(value); snapshot.Commit(); - var mpt0 = new MPTTrie(mpt.GetRoot(), store.GetSnapshot()); + var mpt0 = new MPTTrie(store.GetSnapshot(), mpt.GetRoot()); value = mpt0.Get("ac02".HexToBytes()); Assert.IsNotNull(value); } @@ -252,7 +253,7 @@ public void TestDeleteSameValue() public void TestBranchNodeRemainValue() { var store = new MemoryStore(); - var mpt = new MPTTrie(null, store.GetSnapshot()); + var mpt = new MPTTrie(store.GetSnapshot(), null); var result = mpt.Put("ac11".HexToBytes(), "ac11".HexToBytes()); Assert.IsTrue(result); result = mpt.Put("ac22".HexToBytes(), "ac22".HexToBytes()); @@ -293,7 +294,7 @@ public void TestGetProof() l2.Next = v2; b.Children[10] = l3; - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); + var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); Assert.AreEqual(r.Hash.ToString(), mpt.GetRoot().ToString()); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); @@ -307,7 +308,7 @@ public void TestGetProof() [TestMethod] public void TestVerifyProof() { - var mpt = new MPTTrie(rootHash, mptdb.GetSnapshot()); + var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); TestValue value = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof); @@ -320,7 +321,7 @@ public void TestAddLongerKey() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt = new MPTTrie(null, snapshot); + var mpt = new MPTTrie(snapshot, null); var result = mpt.Put(new byte[] { 0xab }, new byte[] { 0x01 }); Assert.IsTrue(result); result = mpt.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x02 }); @@ -332,12 +333,12 @@ public void TestSplitKey() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt1 = new MPTTrie(null, snapshot); + var mpt1 = new MPTTrie(snapshot, null); Assert.IsTrue(mpt1.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); Assert.IsTrue(mpt1.Put(new byte[] { 0xab }, new byte[] { 0x02 })); Assert.IsTrue(mpt1.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set1)); Assert.AreEqual(4, set1.Count); - var mpt2 = new MPTTrie(null, snapshot); + var mpt2 = new MPTTrie(snapshot, null); Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x02 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); Assert.IsTrue(mpt2.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set2)); @@ -350,10 +351,10 @@ public void TestFind() { var store = new MemoryStore(); var snapshot = store.GetSnapshot(); - var mpt1 = new MPTTrie(null, snapshot); + var mpt1 = new MPTTrie(snapshot, null); var results = mpt1.Find(ReadOnlySpan.Empty).ToArray(); Assert.AreEqual(0, results.Count()); - var mpt2 = new MPTTrie(null, snapshot); + var mpt2 = new MPTTrie(snapshot, null); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xef }, new byte[] { 0x01 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xe1 }, new byte[] { 0x02 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x03 })); From 2d21d44bc9db26d8a343b8a362ad2a40a8657590 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 16:17:39 +0800 Subject: [PATCH 155/171] Remove MPTTrie.GetRoot() --- src/neo/Cryptography/MPT/MPTTrie.cs | 7 ++----- tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs | 10 +++++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index bcb3fc7e5b..a614447321 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -13,6 +13,8 @@ public partial class MPTTrie private readonly ISnapshot store; private MPTNode root; + public MPTNode Root => root; + public MPTTrie(ISnapshot store, UInt256 root) { this.store = store ?? throw new ArgumentNullException(); @@ -25,11 +27,6 @@ public MPTNode Resolve(HashNode n) return MPTNode.Decode(data); } - public UInt256 GetRoot() - { - return root.Hash; - } - protected static byte[] ToNibbles(ReadOnlySpan path) { var result = new byte[path.Length * 2]; diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index 917c1bc4c4..2059d820e7 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -181,7 +181,7 @@ public void TestTryPut() Assert.IsTrue(result); result = mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); Assert.IsTrue(result); - Assert.AreEqual(rootHash.ToString(), mpt.GetRoot().ToString()); + Assert.AreEqual(rootHash.ToString(), mpt.Root.Hash.ToString()); } [TestMethod] @@ -222,7 +222,7 @@ public void TestTryDelete() Assert.IsTrue(result); result = mpt.Delete("acae".HexToBytes()); Assert.IsTrue(result); - Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", mpt.GetRoot().ToString()); + Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", mpt.Root.Hash.ToString()); } [TestMethod] @@ -244,7 +244,7 @@ public void TestDeleteSameValue() Assert.IsNotNull(value); snapshot.Commit(); - var mpt0 = new MPTTrie(store.GetSnapshot(), mpt.GetRoot()); + var mpt0 = new MPTTrie(store.GetSnapshot(), mpt.Root.Hash); value = mpt0.Get("ac02".HexToBytes()); Assert.IsNotNull(value); } @@ -295,7 +295,7 @@ public void TestGetProof() b.Children[10] = l3; var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); - Assert.AreEqual(r.Hash.ToString(), mpt.GetRoot().ToString()); + Assert.AreEqual(r.Hash.ToString(), mpt.Root.Hash.ToString()); var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); Assert.IsTrue(result); Assert.AreEqual(4, proof.Count); @@ -343,7 +343,7 @@ public void TestSplitKey() Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); Assert.IsTrue(mpt2.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set2)); Assert.AreEqual(4, set2.Count); - Assert.AreEqual(mpt1.GetRoot(), mpt2.GetRoot()); + Assert.AreEqual(mpt1.Root.Hash, mpt2.Root.Hash); } [TestMethod] From 86dd27026b1ee3eb7b85418243312bd09947ff92 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 16:19:38 +0800 Subject: [PATCH 156/171] Update MPTTrie.cs --- src/neo/Cryptography/MPT/MPTTrie.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index a614447321..3ab8292753 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -21,13 +21,13 @@ public MPTTrie(ISnapshot store, UInt256 root) this.root = root is null ? HashNode.EmptyNode : new HashNode(root); } - public MPTNode Resolve(HashNode n) + private MPTNode Resolve(HashNode n) { var data = store.TryGet(Prefix, n.Hash.ToArray()); return MPTNode.Decode(data); } - protected static byte[] ToNibbles(ReadOnlySpan path) + private static byte[] ToNibbles(ReadOnlySpan path) { var result = new byte[path.Length * 2]; for (int i = 0; i < path.Length; i++) From 2a8bb6fd380e4b56c25e12d6a1d12b47e7bc8016 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 16:22:58 +0800 Subject: [PATCH 157/171] Simplify declaring the partial class --- src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 4 +--- src/neo/Cryptography/MPT/MPTTrie.Find.cs | 4 +--- src/neo/Cryptography/MPT/MPTTrie.Get.cs | 4 +--- src/neo/Cryptography/MPT/MPTTrie.Proof.cs | 4 +--- src/neo/Cryptography/MPT/MPTTrie.Put.cs | 4 +--- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index f43cbe9a1e..13ef0185b3 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -5,9 +5,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTTrie - where TKey : notnull, ISerializable, new() - where TValue : class, ISerializable, new() + partial class MPTTrie { public bool Delete(TKey key) { diff --git a/src/neo/Cryptography/MPT/MPTTrie.Find.cs b/src/neo/Cryptography/MPT/MPTTrie.Find.cs index cfa92dbbce..dbe4d88ec5 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Find.cs @@ -6,9 +6,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTTrie - where TKey : notnull, ISerializable, new() - where TValue : class, ISerializable, new() + partial class MPTTrie { private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out MPTNode start) { diff --git a/src/neo/Cryptography/MPT/MPTTrie.Get.cs b/src/neo/Cryptography/MPT/MPTTrie.Get.cs index 7340815ed5..80eea4b897 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Get.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Get.cs @@ -3,9 +3,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTTrie - where TKey : notnull, ISerializable, new() - where TValue : class, ISerializable, new() + partial class MPTTrie { public TValue Get(TKey key) { diff --git a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs index 3e76a5d295..0d2db89d3e 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -5,9 +5,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTTrie - where TKey : notnull, ISerializable, new() - where TValue : class, ISerializable, new() + partial class MPTTrie { public bool GetProof(TKey key, out HashSet set) { diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs index 78c0b657b4..cc1616c987 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -3,9 +3,7 @@ namespace Neo.Cryptography.MPT { - public partial class MPTTrie - where TKey : notnull, ISerializable, new() - where TValue : class, ISerializable, new() + partial class MPTTrie { private static ReadOnlySpan CommonPrefix(ReadOnlySpan a, ReadOnlySpan b) { From 6801a80457ec3d1ef36111f56c9734c67f342c43 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 16:25:45 +0800 Subject: [PATCH 158/171] Move PutToStore() --- src/neo/Cryptography/MPT/MPTTrie.Put.cs | 5 ----- src/neo/Cryptography/MPT/MPTTrie.cs | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs index cc1616c987..b6cb95d0db 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -154,10 +154,5 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) return false; } } - - private void PutToStore(MPTNode node) - { - store.Put(Prefix, node.Hash.ToArray(), node.Encode()); - } } } diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index 3ab8292753..234c100859 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -37,5 +37,10 @@ private static byte[] ToNibbles(ReadOnlySpan path) } return result; } + + private void PutToStore(MPTNode node) + { + store.Put(Prefix, node.Hash.ToArray(), node.Encode()); + } } } From ded26b950b7971721153d355150f3c08f5d1f0d4 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 16:39:15 +0800 Subject: [PATCH 159/171] Remove MPTTrie.Get() --- src/neo/Cryptography/MPT/MPTTrie.Get.cs | 21 +++----- src/neo/Cryptography/MPT/MPTTrie.Proof.cs | 2 +- .../Cryptography/MPT/UT_MPTTrie.cs | 49 ++++++------------- 3 files changed, 23 insertions(+), 49 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Get.cs b/src/neo/Cryptography/MPT/MPTTrie.Get.cs index 80eea4b897..4736a0e354 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Get.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Get.cs @@ -5,12 +5,15 @@ namespace Neo.Cryptography.MPT { partial class MPTTrie { - public TValue Get(TKey key) + public TValue this[TKey key] { - var path = ToNibbles(key.ToArray()); - if (path.Length < 1) return null; - var result = TryGet(ref root, path, out var value); - return result ? value.AsSerializable() : null; + get + { + var path = ToNibbles(key.ToArray()); + if (path.Length < 1) return null; + var result = TryGet(ref root, path, out var value); + return result ? value.AsSerializable() : null; + } } private bool TryGet(ref MPTNode node, ReadOnlySpan path, out ReadOnlySpan value) @@ -54,13 +57,5 @@ private bool TryGet(ref MPTNode node, ReadOnlySpan path, out ReadOnlySpan< value = default; return false; } - - public TValue this[TKey key] - { - get - { - return Get(key); - } - } } } diff --git a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs index 0d2db89d3e..6f70a5057b 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -71,7 +71,7 @@ public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) { snapshot.Put(Prefix, Crypto.Hash256(data), data); } - return trie.Get(key); + return trie[key]; } } } diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index 2059d820e7..c821805b31 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -140,34 +140,19 @@ public void TestInit() public void TestTryGet() { var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); - TestValue value = mpt.Get("ac01".HexToBytes()); - Assert.IsNotNull(value); - Assert.AreEqual("abcd", value.ToString()); - - value = mpt.Get("ac99".HexToBytes()); - Assert.IsNotNull(value); - Assert.AreEqual("2222", value.ToString()); - - value = mpt.Get("ab99".HexToBytes()); - Assert.IsNull(value); - - value = mpt.Get("ac39".HexToBytes()); - Assert.IsNull(value); - - value = mpt.Get("ac02".HexToBytes()); - Assert.IsNull(value); - - value = mpt.Get("ac9910".HexToBytes()); - Assert.IsNull(value); + Assert.AreEqual("abcd", mpt["ac01".HexToBytes()].ToString()); + Assert.AreEqual("2222", mpt["ac99".HexToBytes()].ToString()); + Assert.IsNull(mpt["ab99".HexToBytes()]); + Assert.IsNull(mpt["ac39".HexToBytes()]); + Assert.IsNull(mpt["ac02".HexToBytes()]); + Assert.IsNull(mpt["ac9910".HexToBytes()]); } [TestMethod] public void TestTryGetResolve() { var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); - TestValue value = mpt.Get("acae".HexToBytes()); - Assert.IsNotNull(value); - Assert.AreEqual(Encoding.ASCII.GetBytes("hello").ToHexString(), value.ToString()); + Assert.AreEqual(Encoding.ASCII.GetBytes("hello").ToHexString(), mpt["acae".HexToBytes()].ToString()); } [TestMethod] @@ -215,10 +200,8 @@ public void TestTryDelete() Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.Hash.ToString()); var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); - var result = true; - TestValue value = mpt.Get("ac99".HexToBytes()); - Assert.IsNotNull(value); - result = mpt.Delete("ac99".HexToBytes()); + Assert.IsNotNull(mpt["ac99".HexToBytes()]); + bool result = mpt.Delete("ac99".HexToBytes()); Assert.IsTrue(result); result = mpt.Delete("acae".HexToBytes()); Assert.IsTrue(result); @@ -235,18 +218,14 @@ public void TestDeleteSameValue() Assert.IsTrue(result); result = mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes()); Assert.IsTrue(result); - TestValue value = mpt.Get("ac01".HexToBytes()); - Assert.IsNotNull(value); - value = mpt.Get("ac02".HexToBytes()); - Assert.IsNotNull(value); + Assert.IsNotNull(mpt["ac01".HexToBytes()]); + Assert.IsNotNull(mpt["ac02".HexToBytes()]); result = mpt.Delete("ac01".HexToBytes()); - value = mpt.Get("ac02".HexToBytes()); - Assert.IsNotNull(value); + Assert.IsNotNull(mpt["ac02".HexToBytes()]); snapshot.Commit(); var mpt0 = new MPTTrie(store.GetSnapshot(), mpt.Root.Hash); - value = mpt0.Get("ac02".HexToBytes()); - Assert.IsNotNull(value); + Assert.IsNotNull(mpt0["ac02".HexToBytes()]); } [TestMethod] @@ -263,7 +242,7 @@ public void TestBranchNodeRemainValue() result = mpt.Delete("ac11".HexToBytes()); Assert.IsTrue(result); result = mpt.Delete("ac22".HexToBytes()); - Assert.IsFalse(mpt.Get("ac".HexToBytes()) is null); + Assert.IsNotNull(mpt["ac".HexToBytes()]); } [TestMethod] From 497dcd84ec1f9964afd1e1b535a1aaa30a88e14a Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 16:44:39 +0800 Subject: [PATCH 160/171] Modify MPTTrie.GetProof() --- src/neo/Cryptography/MPT/MPTTrie.Proof.cs | 9 +++++---- tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs | 10 ++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs index 6f70a5057b..f089e72b23 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -7,12 +7,13 @@ namespace Neo.Cryptography.MPT { partial class MPTTrie { - public bool GetProof(TKey key, out HashSet set) + public HashSet GetProof(TKey key) { - set = new HashSet(ByteArrayEqualityComparer.Default); var path = ToNibbles(key.ToArray()); - if (path.Length < 1) return false; - return GetProof(ref root, path, set); + if (path.Length < 1) return null; + HashSet set = new HashSet(ByteArrayEqualityComparer.Default); + if (!GetProof(ref root, path, set)) return null; + return set; } private bool GetProof(ref MPTNode node, ReadOnlySpan path, HashSet set) diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index c821805b31..92e1af982c 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -275,8 +275,7 @@ public void TestGetProof() var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); Assert.AreEqual(r.Hash.ToString(), mpt.Root.Hash.ToString()); - var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); - Assert.IsTrue(result); + HashSet proof = mpt.GetProof("ac01".HexToBytes()); Assert.AreEqual(4, proof.Count); Assert.IsTrue(proof.Contains(b.Encode())); Assert.IsTrue(proof.Contains(r.Encode())); @@ -288,8 +287,7 @@ public void TestGetProof() public void TestVerifyProof() { var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); - var result = mpt.GetProof("ac01".HexToBytes(), out HashSet proof); - Assert.IsTrue(result); + HashSet proof = mpt.GetProof("ac01".HexToBytes()); TestValue value = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof); Assert.IsNotNull(value); Assert.AreEqual(value.ToString(), "abcd"); @@ -315,12 +313,12 @@ public void TestSplitKey() var mpt1 = new MPTTrie(snapshot, null); Assert.IsTrue(mpt1.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); Assert.IsTrue(mpt1.Put(new byte[] { 0xab }, new byte[] { 0x02 })); - Assert.IsTrue(mpt1.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set1)); + HashSet set1 = mpt1.GetProof(new byte[] { 0xab, 0xcd }); Assert.AreEqual(4, set1.Count); var mpt2 = new MPTTrie(snapshot, null); Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x02 })); Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); - Assert.IsTrue(mpt2.GetProof(new byte[] { 0xab, 0xcd }, out HashSet set2)); + HashSet set2 = mpt2.GetProof(new byte[] { 0xab, 0xcd }); Assert.AreEqual(4, set2.Count); Assert.AreEqual(mpt1.Root.Hash, mpt2.Root.Hash); } From c27d094ebe018fa5e1e6a95cfc6e1561d3cec2ef Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 20 May 2020 17:02:41 +0800 Subject: [PATCH 161/171] Format --- src/neo/Cryptography/MPT/BranchNode.cs | 2 - .../Cryptography/MPT/UT_MPTNode.cs | 6 +- .../Cryptography/MPT/UT_MPTTrie.cs | 119 ++++++------------ 3 files changed, 40 insertions(+), 87 deletions(-) diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs index 5622cd6f2c..e69f2130ab 100644 --- a/src/neo/Cryptography/MPT/BranchNode.cs +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -1,6 +1,4 @@ -using Neo.IO.Json; using System.IO; -using System.Linq; namespace Neo.Cryptography.MPT { diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs index eb06ad64e5..6ddc00f4be 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs @@ -10,8 +10,10 @@ public class UT_MPTNode [TestMethod] public void TestDecode() { - var n = new LeafNode(); - n.Value = Encoding.ASCII.GetBytes("hello"); + var n = new LeafNode + { + Value = Encoding.ASCII.GetBytes("hello") + }; var code = n.Encode(); var m = MPTNode.Decode(code); Assert.IsInstanceOfType(m, n.GetType()); diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index 92e1af982c..bf4a841748 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -89,8 +89,6 @@ public class UT_MPTTrie private MPTNode root; private IStore mptdb; - private UInt256 rootHash; - private void PutToStore(MPTNode node) { mptdb.Put(0xf0, node.Hash.ToArray(), node.Encode()); @@ -99,33 +97,20 @@ private void PutToStore(MPTNode node) [TestInitialize] public void TestInit() { - var r = new ExtensionNode(); - r.Key = "0a0c".HexToBytes(); var b = new BranchNode(); - var l1 = new ExtensionNode(); - l1.Key = new byte[] { 0x01 }; - var l2 = new ExtensionNode(); - l2.Key = new byte[] { 0x09 }; - var v1 = new LeafNode(); - v1.Value = "abcd".HexToBytes(); - var v2 = new LeafNode(); - v2.Value = "2222".HexToBytes(); - var v3 = new LeafNode(); - v3.Value = Encoding.ASCII.GetBytes("hello"); + var r = new ExtensionNode { Key = "0a0c".HexToBytes(), Next = b }; + var v1 = new LeafNode { Value = "abcd".HexToBytes() }; + var v2 = new LeafNode { Value = "2222".HexToBytes() }; + var v3 = new LeafNode { Value = Encoding.ASCII.GetBytes("hello") }; var h1 = new HashNode(v3.Hash); - var l3 = new ExtensionNode(); - l3.Next = h1; - l3.Key = "0e".HexToBytes(); - - r.Next = b; + var l1 = new ExtensionNode { Key = new byte[] { 0x01 }, Next = v1 }; + var l2 = new ExtensionNode { Key = new byte[] { 0x09 }, Next = v2 }; + var l3 = new ExtensionNode { Key = "0e".HexToBytes(), Next = h1 }; b.Children[0] = l1; - l1.Next = v1; b.Children[9] = l2; - l2.Next = v2; b.Children[10] = l3; - root = r; + this.root = r; this.mptdb = new MemoryStore(); - this.rootHash = root.Hash; PutToStore(r); PutToStore(b); PutToStore(l1); @@ -139,7 +124,7 @@ public void TestInit() [TestMethod] public void TestTryGet() { - var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); Assert.AreEqual("abcd", mpt["ac01".HexToBytes()].ToString()); Assert.AreEqual("2222", mpt["ac99".HexToBytes()].ToString()); Assert.IsNull(mpt["ab99".HexToBytes()]); @@ -151,7 +136,7 @@ public void TestTryGet() [TestMethod] public void TestTryGetResolve() { - var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); Assert.AreEqual(Encoding.ASCII.GetBytes("hello").ToHexString(), mpt["acae".HexToBytes()].ToString()); } @@ -166,40 +151,26 @@ public void TestTryPut() Assert.IsTrue(result); result = mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); Assert.IsTrue(result); - Assert.AreEqual(rootHash.ToString(), mpt.Root.Hash.ToString()); + Assert.AreEqual(root.Hash.ToString(), mpt.Root.Hash.ToString()); } [TestMethod] public void TestTryDelete() { - var r1 = new ExtensionNode(); - r1.Key = "0a0c0001".HexToBytes(); - - var r = new ExtensionNode(); - r.Key = "0a0c".HexToBytes(); - var b = new BranchNode(); - r.Next = b; - - var l1 = new ExtensionNode(); - l1.Key = new byte[] { 0x01 }; - var v1 = new LeafNode(); - v1.Value = "abcd".HexToBytes(); - l1.Next = v1; + var r = new ExtensionNode { Key = "0a0c".HexToBytes(), Next = b }; + var v1 = new LeafNode { Value = "abcd".HexToBytes() }; + var v2 = new LeafNode { Value = "2222".HexToBytes() }; + var r1 = new ExtensionNode { Key = "0a0c0001".HexToBytes(), Next = v1 }; + var l1 = new ExtensionNode { Key = new byte[] { 0x01 }, Next = v1 }; + var l2 = new ExtensionNode { Key = new byte[] { 0x09 }, Next = v2 }; b.Children[0] = l1; - - var l2 = new ExtensionNode(); - l2.Key = new byte[] { 0x09 }; - var v2 = new LeafNode(); - v2.Value = "2222".HexToBytes(); - l2.Next = v2; b.Children[9] = l2; - r1.Next = v1; Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.Hash.ToString()); Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.Hash.ToString()); - var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); Assert.IsNotNull(mpt["ac99".HexToBytes()]); bool result = mpt.Delete("ac99".HexToBytes()); Assert.IsTrue(result); @@ -214,13 +185,11 @@ public void TestDeleteSameValue() var store = new MemoryStore(); var snapshot = store.GetSnapshot(); var mpt = new MPTTrie(snapshot, null); - var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); - Assert.IsTrue(result); - result = mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes()); - Assert.IsTrue(result); + Assert.IsTrue(mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes())); + Assert.IsTrue(mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes())); Assert.IsNotNull(mpt["ac01".HexToBytes()]); Assert.IsNotNull(mpt["ac02".HexToBytes()]); - result = mpt.Delete("ac01".HexToBytes()); + mpt.Delete("ac01".HexToBytes()); Assert.IsNotNull(mpt["ac02".HexToBytes()]); snapshot.Commit(); @@ -233,47 +202,31 @@ public void TestBranchNodeRemainValue() { var store = new MemoryStore(); var mpt = new MPTTrie(store.GetSnapshot(), null); - var result = mpt.Put("ac11".HexToBytes(), "ac11".HexToBytes()); - Assert.IsTrue(result); - result = mpt.Put("ac22".HexToBytes(), "ac22".HexToBytes()); - Assert.IsTrue(result); - result = mpt.Put("ac".HexToBytes(), "ac".HexToBytes()); - Assert.IsTrue(result); - result = mpt.Delete("ac11".HexToBytes()); - Assert.IsTrue(result); - result = mpt.Delete("ac22".HexToBytes()); + Assert.IsTrue(mpt.Put("ac11".HexToBytes(), "ac11".HexToBytes())); + Assert.IsTrue(mpt.Put("ac22".HexToBytes(), "ac22".HexToBytes())); + Assert.IsTrue(mpt.Put("ac".HexToBytes(), "ac".HexToBytes())); + Assert.IsTrue(mpt.Delete("ac11".HexToBytes())); + mpt.Delete("ac22".HexToBytes()); Assert.IsNotNull(mpt["ac".HexToBytes()]); } [TestMethod] public void TestGetProof() { - var r = new ExtensionNode(); - r.Key = "0a0c".HexToBytes(); var b = new BranchNode(); - var l1 = new ExtensionNode(); - l1.Key = new byte[] { 0x01 }; - var l2 = new ExtensionNode(); - l2.Key = new byte[] { 0x09 }; - var v1 = new LeafNode(); - v1.Value = "abcd".HexToBytes(); - var v2 = new LeafNode(); - v2.Value = "2222".HexToBytes(); - var v3 = new LeafNode(); - v3.Value = Encoding.ASCII.GetBytes("hello"); + var r = new ExtensionNode { Key = "0a0c".HexToBytes(), Next = b }; + var v1 = new LeafNode { Value = "abcd".HexToBytes() }; + var v2 = new LeafNode { Value = "2222".HexToBytes() }; + var v3 = new LeafNode { Value = Encoding.ASCII.GetBytes("hello") }; var h1 = new HashNode(v3.Hash); - var l3 = new ExtensionNode(); - l3.Next = h1; - l3.Key = "0e".HexToBytes(); - - r.Next = b; + var l1 = new ExtensionNode { Key = new byte[] { 0x01 }, Next = v1 }; + var l2 = new ExtensionNode { Key = new byte[] { 0x09 }, Next = v2 }; + var l3 = new ExtensionNode { Key = "0e".HexToBytes(), Next = h1 }; b.Children[0] = l1; - l1.Next = v1; b.Children[9] = l2; - l2.Next = v2; b.Children[10] = l3; - var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); Assert.AreEqual(r.Hash.ToString(), mpt.Root.Hash.ToString()); HashSet proof = mpt.GetProof("ac01".HexToBytes()); Assert.AreEqual(4, proof.Count); @@ -286,9 +239,9 @@ public void TestGetProof() [TestMethod] public void TestVerifyProof() { - var mpt = new MPTTrie(mptdb.GetSnapshot(), rootHash); + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); HashSet proof = mpt.GetProof("ac01".HexToBytes()); - TestValue value = MPTTrie.VerifyProof(rootHash, "ac01".HexToBytes(), proof); + TestValue value = MPTTrie.VerifyProof(root.Hash, "ac01".HexToBytes(), proof); Assert.IsNotNull(value); Assert.AreEqual(value.ToString(), "abcd"); } From df8048d6fe82d5e03ae7e7aefe0ec686857419f3 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Wed, 20 May 2020 18:00:51 +0800 Subject: [PATCH 162/171] rm repeated put --- src/neo/Cryptography/MPT/MPTTrie.Proof.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs index f089e72b23..9b7cdf9950 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -68,10 +68,6 @@ public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) } ISnapshot snapshot = memoryStore.GetSnapshot(); var trie = new MPTTrie(snapshot, root); - foreach (byte[] data in proof) - { - snapshot.Put(Prefix, Crypto.Hash256(data), data); - } return trie[key]; } } From bd2011f083090d064a04f0b6fb82ca028f655d1d Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 21 May 2020 19:33:14 +0800 Subject: [PATCH 163/171] using --- src/neo/Cryptography/MPT/MPTTrie.Proof.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs index 9b7cdf9950..7811354cc8 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -61,12 +61,10 @@ private bool GetProof(ref MPTNode node, ReadOnlySpan path, HashSet public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) { - var memoryStore = new MemoryStore(); + using var memoryStore = new MemoryStore(); foreach (byte[] data in proof) - { memoryStore.Put(Prefix, Crypto.Hash256(data), data); - } - ISnapshot snapshot = memoryStore.GetSnapshot(); + using ISnapshot snapshot = memoryStore.GetSnapshot(); var trie = new MPTTrie(snapshot, root); return trie[key]; } From 6275b0affdcc8f26abe26d63eda912610c83c5e4 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 26 May 2020 13:39:02 +0800 Subject: [PATCH 164/171] Optimize --- src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index 13ef0185b3..05da852ff6 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -61,7 +61,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) result = TryDelete(ref branchNode.Children[path[0]], path[1..]); } if (!result) return false; - List childrenIndexes = new List(); + List childrenIndexes = new List(BranchNode.ChildCount); for (int i = 0; i < BranchNode.ChildCount; i++) { if (branchNode.Children[i] is HashNode hn && hn.IsEmpty) continue; From f915b56da9ad4a21d649d32560cfc5673a607821 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 28 May 2020 13:00:14 +0800 Subject: [PATCH 165/171] fix mpt find --- src/neo/Cryptography/MPT/MPTTrie.Find.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Find.cs b/src/neo/Cryptography/MPT/MPTTrie.Find.cs index dbe4d88ec5..adb307b2e1 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Find.cs @@ -42,7 +42,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M { if (path.Length < 1) { - start = extensionNode; + start = extensionNode.Next; return ReadOnlySpan.Empty; } if (path.StartsWith(extensionNode.Key)) @@ -51,8 +51,8 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M } if (extensionNode.Key.AsSpan().StartsWith(path)) { - start = extensionNode; - return extensionNode.Key[path.Length..]; + start = extensionNode.Next; + return extensionNode.Key; } break; } @@ -71,6 +71,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M private IEnumerable<(byte[] Key, byte[] Value)> Travers(MPTNode node, byte[] path) { + if (node is null) yield break; switch (node) { case LeafNode leafNode: From e5e691b99c62829c13e12de999fbd3fdcc36d22c Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 28 May 2020 13:03:09 +0800 Subject: [PATCH 166/171] recover key from nibles --- src/neo/Cryptography/MPT/MPTTrie.Find.cs | 2 +- src/neo/Cryptography/MPT/MPTTrie.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Find.cs b/src/neo/Cryptography/MPT/MPTTrie.Find.cs index adb307b2e1..6a3fba8213 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Find.cs @@ -66,7 +66,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M var path = ToNibbles(prefix); path = Seek(ref root, path, out MPTNode start).ToArray(); return Travers(start, path) - .Select(p => (p.Key.AsSerializable(), p.Value.AsSerializable())); + .Select(p => (FromNibbles(p.Key).AsSerializable(), p.Value.AsSerializable())); } private IEnumerable<(byte[] Key, byte[] Value)> Travers(MPTNode node, byte[] path) diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs index 234c100859..df97b54f9c 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -38,6 +38,18 @@ private static byte[] ToNibbles(ReadOnlySpan path) return result; } + private static byte[] FromNibbles(ReadOnlySpan path) + { + if (path.Length % 2 != 0) throw new FormatException($"MPTTrie.FromNibbles invalid path."); + var key = new byte[path.Length / 2]; + for (int i = 0; i < key.Length; i++) + { + key[i] = (byte)(path[i * 2] << 4); + key[i] |= path[i * 2 + 1]; + } + return key; + } + private void PutToStore(MPTNode node) { store.Put(Prefix, node.Hash.ToArray(), node.Encode()); From 2bcda02ad92821a6673ca74d270380999d4cfb74 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Thu, 28 May 2020 14:20:07 +0800 Subject: [PATCH 167/171] Readability and format --- src/neo/Cryptography/MPT/MPTNode.cs | 3 +-- src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 6 +++--- src/neo/Cryptography/MPT/MPTTrie.Find.cs | 6 +++--- src/neo/Cryptography/MPT/MPTTrie.Get.cs | 6 +++--- src/neo/Cryptography/MPT/MPTTrie.Proof.cs | 6 +++--- src/neo/Cryptography/MPT/MPTTrie.Put.cs | 14 +++++++------- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs index a98f7b9a4d..85955c625f 100644 --- a/src/neo/Cryptography/MPT/MPTNode.cs +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -1,6 +1,5 @@ using Neo.IO; using Neo.IO.Caching; -using Neo.IO.Json; using System; using System.IO; @@ -34,7 +33,7 @@ public byte[] Encode() public static unsafe MPTNode Decode(ReadOnlySpan data) { - if (data.Length < 1) return null; + if (data.IsEmpty) return null; fixed (byte* pointer = data) { diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs index 05da852ff6..4996738b92 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -10,7 +10,7 @@ partial class MPTTrie public bool Delete(TKey key) { var path = ToNibbles(key.ToArray()); - if (path.Length < 1) return false; + if (path.Length == 0) return false; return TryDelete(ref root, path); } @@ -20,7 +20,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) { case LeafNode _: { - if (path.Length < 1) + if (path.IsEmpty) { node = HashNode.EmptyNode; return true; @@ -52,7 +52,7 @@ private bool TryDelete(ref MPTNode node, ReadOnlySpan path) case BranchNode branchNode: { bool result; - if (path.Length < 1) + if (path.IsEmpty) { result = TryDelete(ref branchNode.Children[BranchNode.ChildCount - 1], path); } diff --git a/src/neo/Cryptography/MPT/MPTTrie.Find.cs b/src/neo/Cryptography/MPT/MPTTrie.Find.cs index 6a3fba8213..f9f699c581 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Find.cs @@ -14,7 +14,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M { case LeafNode leafNode: { - if (path.Length < 1) + if (path.IsEmpty) { start = leafNode; return ReadOnlySpan.Empty; @@ -31,7 +31,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M } case BranchNode branchNode: { - if (path.Length < 1) + if (path.IsEmpty) { start = branchNode; return ReadOnlySpan.Empty; @@ -40,7 +40,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M } case ExtensionNode extensionNode: { - if (path.Length < 1) + if (path.IsEmpty) { start = extensionNode.Next; return ReadOnlySpan.Empty; diff --git a/src/neo/Cryptography/MPT/MPTTrie.Get.cs b/src/neo/Cryptography/MPT/MPTTrie.Get.cs index 4736a0e354..367f2a61ce 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Get.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Get.cs @@ -10,7 +10,7 @@ public TValue this[TKey key] get { var path = ToNibbles(key.ToArray()); - if (path.Length < 1) return null; + if (path.Length == 0) return null; var result = TryGet(ref root, path, out var value); return result ? value.AsSerializable() : null; } @@ -22,7 +22,7 @@ private bool TryGet(ref MPTNode node, ReadOnlySpan path, out ReadOnlySpan< { case LeafNode leafNode: { - if (path.Length < 1) + if (path.IsEmpty) { value = leafNode.Value; return true; @@ -39,7 +39,7 @@ private bool TryGet(ref MPTNode node, ReadOnlySpan path, out ReadOnlySpan< } case BranchNode branchNode: { - if (path.Length < 1) + if (path.IsEmpty) { return TryGet(ref branchNode.Children[BranchNode.ChildCount - 1], path, out value); } diff --git a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs index 7811354cc8..7b4be120ab 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -10,7 +10,7 @@ partial class MPTTrie public HashSet GetProof(TKey key) { var path = ToNibbles(key.ToArray()); - if (path.Length < 1) return null; + if (path.Length == 0) return null; HashSet set = new HashSet(ByteArrayEqualityComparer.Default); if (!GetProof(ref root, path, set)) return null; return set; @@ -22,7 +22,7 @@ private bool GetProof(ref MPTNode node, ReadOnlySpan path, HashSet { case LeafNode leafNode: { - if (path.Length < 1) + if (path.IsEmpty) { set.Add(leafNode.Encode()); return true; @@ -40,7 +40,7 @@ private bool GetProof(ref MPTNode node, ReadOnlySpan path, HashSet case BranchNode branchNode: { set.Add(branchNode.Encode()); - if (path.Length < 1) + if (path.IsEmpty) { return GetProof(ref branchNode.Children[BranchNode.ChildCount - 1], path, set); } diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs index b6cb95d0db..491213916f 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Put.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -23,11 +23,11 @@ public bool Put(TKey key, TValue value) { var path = ToNibbles(key.ToArray()); var val = value.ToArray(); - if (ExtensionNode.MaxKeyLength < path.Length || path.Length < 1) + if (path.Length == 0 || path.Length > ExtensionNode.MaxKeyLength) return false; - if (LeafNode.MaxValueLength < val.Length) + if (val.Length > LeafNode.MaxValueLength) return false; - if (val.Length < 1) + if (val.Length == 0) return TryDelete(ref root, path); var n = new LeafNode(val); return Put(ref root, path, n); @@ -41,7 +41,7 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) { if (val is LeafNode v) { - if (path.Length < 1) + if (path.IsEmpty) { node = v; PutToStore(node); @@ -78,7 +78,7 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) Put(ref grandSon1, keyRemain[1..], extensionNode.Next); son.Children[keyRemain[0]] = grandSon1; - if (pathRemain.Length < 1) + if (pathRemain.IsEmpty) { Put(ref grandSon2, pathRemain, val); son.Children[BranchNode.ChildCount - 1] = grandSon2; @@ -108,7 +108,7 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) case BranchNode branchNode: { bool result; - if (path.Length < 1) + if (path.IsEmpty) { result = Put(ref branchNode.Children[BranchNode.ChildCount - 1], path, val); } @@ -128,7 +128,7 @@ private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) MPTNode newNode; if (hashNode.IsEmpty) { - if (path.Length < 1) + if (path.IsEmpty) { newNode = val; } From d0e33c46f29f1237a2ac66a8508c69e3d99dfb42 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Thu, 28 May 2020 14:25:03 +0800 Subject: [PATCH 168/171] fix find --- src/neo/Cryptography/MPT/MPTTrie.Find.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Cryptography/MPT/MPTTrie.Find.cs b/src/neo/Cryptography/MPT/MPTTrie.Find.cs index 6a3fba8213..53ffdb4ebb 100644 --- a/src/neo/Cryptography/MPT/MPTTrie.Find.cs +++ b/src/neo/Cryptography/MPT/MPTTrie.Find.cs @@ -43,7 +43,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M if (path.Length < 1) { start = extensionNode.Next; - return ReadOnlySpan.Empty; + return extensionNode.Key; } if (path.StartsWith(extensionNode.Key)) { @@ -93,7 +93,7 @@ private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out M { for (int i = 0; i < BranchNode.ChildCount; i++) { - foreach (var item in Travers(branchNode.Children[i], Concat(path, new byte[] { (byte)i }))) + foreach (var item in Travers(branchNode.Children[i], i == BranchNode.ChildCount - 1 ? path : Concat(path, new byte[] { (byte)i }))) yield return item; } break; From f35103b8fd12f298495e2c39c405180f1a2c7db1 Mon Sep 17 00:00:00 2001 From: Tommo-L Date: Thu, 28 May 2020 21:09:26 +0800 Subject: [PATCH 169/171] enhance ut --- src/neo/Cryptography/MPT/ExtensionNode.cs | 1 - src/neo/Cryptography/MPT/HashNode.cs | 1 - src/neo/Cryptography/MPT/LeafNode.cs | 1 - tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs | 12 ++++++++++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/neo/Cryptography/MPT/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs index 322eb29b70..def3aa9a1b 100644 --- a/src/neo/Cryptography/MPT/ExtensionNode.cs +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.IO.Json; using Neo.SmartContract; using System.IO; diff --git a/src/neo/Cryptography/MPT/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs index d4b034937a..b304833d66 100644 --- a/src/neo/Cryptography/MPT/HashNode.cs +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.IO.Json; using System; using System.IO; diff --git a/src/neo/Cryptography/MPT/LeafNode.cs b/src/neo/Cryptography/MPT/LeafNode.cs index 651bbcd409..3e3b60b9da 100644 --- a/src/neo/Cryptography/MPT/LeafNode.cs +++ b/src/neo/Cryptography/MPT/LeafNode.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.IO.Json; using Neo.SmartContract; using System; using System.IO; diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index bf4a841748..43ed63fe7d 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -297,5 +297,17 @@ public void TestFind() results = mpt2.Find(new byte[] { 0xac }).ToArray(); Assert.AreEqual(0, results.Count()); } + + [TestMethod] + public void TestFindLeadNode() + { + // r.Key = 0x0a0c + // b.Key = 0x00 + // l1.Key = 0x01 + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); + var prefix = new byte[] { 0xac, 0x01 }; // = FromNibbles(path = { 0x0a, 0x0c, 0x00, 0x01 }); + var results = mpt.Find(prefix).ToArray(); + Assert.AreEqual(1, results.Count()); + } } } From 528283b4887635d6971bc54a6c89544747b426a0 Mon Sep 17 00:00:00 2001 From: Tommo-L Date: Thu, 28 May 2020 21:20:23 +0800 Subject: [PATCH 170/171] enhance ut(2) --- tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs index 43ed63fe7d..014a10e6d2 100644 --- a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -308,6 +308,10 @@ public void TestFindLeadNode() var prefix = new byte[] { 0xac, 0x01 }; // = FromNibbles(path = { 0x0a, 0x0c, 0x00, 0x01 }); var results = mpt.Find(prefix).ToArray(); Assert.AreEqual(1, results.Count()); + + prefix = new byte[] { 0xac }; // = FromNibbles(path = { 0x0a, 0x0c }); + results = mpt.Find(prefix).ToArray(); + Assert.AreEqual(3, results.Count()); } } } From eb3722225f3781d2af29990c1ea82e1fd76e3e55 Mon Sep 17 00:00:00 2001 From: KickSeason Date: Mon, 1 Jun 2020 09:40:46 +0800 Subject: [PATCH 171/171] sync with master --- src/neo/Cryptography/MPT/ExtensionNode.cs | 2 +- src/neo/Cryptography/MPT/LeafNode.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Cryptography/MPT/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs index def3aa9a1b..9c575915cf 100644 --- a/src/neo/Cryptography/MPT/ExtensionNode.cs +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -7,7 +7,7 @@ namespace Neo.Cryptography.MPT public class ExtensionNode : MPTNode { //max lenght when store StorageKey - public const int MaxKeyLength = (InteropService.Storage.MaxKeySize + sizeof(int)) * 2; + public const int MaxKeyLength = (ApplicationEngine.MaxStorageValueSize + sizeof(int)) * 2; public byte[] Key; public MPTNode Next; diff --git a/src/neo/Cryptography/MPT/LeafNode.cs b/src/neo/Cryptography/MPT/LeafNode.cs index 3e3b60b9da..cf95a5bc63 100644 --- a/src/neo/Cryptography/MPT/LeafNode.cs +++ b/src/neo/Cryptography/MPT/LeafNode.cs @@ -8,7 +8,7 @@ namespace Neo.Cryptography.MPT public class LeafNode : MPTNode { //the max size when store StorageItem - public const int MaxValueLength = 3 + InteropService.Storage.MaxValueSize + sizeof(bool); + public const int MaxValueLength = 3 + ApplicationEngine.MaxStorageValueSize + sizeof(bool); public byte[] Value;