diff --git a/src/Neo.VM/Types/CompoundType.cs b/src/Neo.VM/Types/CompoundType.cs index 6b0da819ec..33f670bfcb 100644 --- a/src/Neo.VM/Types/CompoundType.cs +++ b/src/Neo.VM/Types/CompoundType.cs @@ -60,12 +60,32 @@ public sealed override bool GetBoolean() } /// - /// The operation is not supported. Always throw . + /// + /// This method provides a hash code for the based on its item's span. + /// It is used for efficient storage and retrieval in hash-based collections. + /// + /// Use this method when you need a hash code for a . /// - /// This method always throws the exception. + /// The hash code for the . public override int GetHashCode() { - throw new NotSupportedException(); + var h = new HashCode(); + h.Add(Count); + h.Add(Type); + foreach (var item in SubItems) + { + // This isn't prefect and leaves somethings unsolved. + if (item is CompoundType cItem) + { + h.Add(cItem.Count); + h.Add(cItem.Type); + } + else + { + h.Add(item.GetHashCode()); + } + } + return h.ToHashCode(); } public override string ToString() diff --git a/tests/Neo.VM.Tests/UT_StackItem.cs b/tests/Neo.VM.Tests/UT_StackItem.cs index 6982f2463a..61f6de74a5 100644 --- a/tests/Neo.VM.Tests/UT_StackItem.cs +++ b/tests/Neo.VM.Tests/UT_StackItem.cs @@ -19,6 +19,21 @@ namespace Neo.Test [TestClass] public class UT_StackItem { + [TestMethod] + public void TestCircularReference() + { + var itemA = new Struct { true, false }; + var itemB = new Struct { true, false }; + var itemC = new Struct { false, false }; + + itemA[1] = itemA; + itemB[1] = itemB; + itemC[1] = itemC; + + Assert.AreEqual(itemA.GetHashCode(), itemB.GetHashCode()); + Assert.AreNotEqual(itemA.GetHashCode(), itemC.GetHashCode()); + } + [TestMethod] public void TestHashCode() { @@ -62,17 +77,35 @@ public void TestHashCode() Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); - itemA = new VM.Types.Array(); + itemA = new Array { true, false, 0 }; + itemB = new Array { true, false, 0 }; + itemC = new Array { true, false, 1 }; + + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); + + itemA = new Struct { true, false, 0 }; + itemB = new Struct { true, false, 0 }; + itemC = new Struct { true, false, 1 }; - Assert.ThrowsException(() => itemA.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); - itemA = new Struct(); + itemA = new Map { [true] = false, [0] = 1 }; + itemB = new Map { [true] = false, [0] = 1 }; + itemC = new Map { [true] = false, [0] = 2 }; - Assert.ThrowsException(() => itemA.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); - itemA = new Map(); + // Test CompoundType GetHashCode for subitems + var junk = new Array { true, false, 0 }; + itemA = new Map { [true] = junk, [0] = junk }; + itemB = new Map { [true] = junk, [0] = junk }; + itemC = new Map { [true] = junk, [0] = 2 }; - Assert.ThrowsException(() => itemA.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() == itemB.GetHashCode()); + Assert.IsTrue(itemA.GetHashCode() != itemC.GetHashCode()); itemA = new InteropInterface(123); itemB = new InteropInterface(123);