From dfce4815d8fccd9638a2f5984b8e1620643b5737 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 21 Dec 2020 16:34:42 +0800 Subject: [PATCH 01/65] Add System.Storage.FindKeys (#2169) * Add System.Storage.FindKeys * Add ut * Shargon's suggestion Co-authored-by: Shargon --- .../ApplicationEngine.Storage.cs | 12 +++- .../Enumerators/StorageKeyEnumerator.cs | 36 ++++++++++ .../Enumerators/UT_StorageKeyEnumerator.cs | 69 +++++++++++++++++++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 src/neo/SmartContract/Enumerators/StorageKeyEnumerator.cs create mode 100644 tests/neo.UnitTests/SmartContract/Enumerators/UT_StorageKeyEnumerator.cs diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index 4401bc3b31..f11ddd2187 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -1,4 +1,5 @@ using Neo.Ledger; +using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; using Neo.SmartContract.Native; using System; @@ -16,6 +17,7 @@ partial class ApplicationEngine public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 1 << 4, CallFlags.ReadStates, false); public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 1 << 15, CallFlags.ReadStates, false); public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 1 << 15, CallFlags.ReadStates, false); + public static readonly InteropDescriptor System_Storage_FindKeys = Register("System.Storage.FindKeys", nameof(FindKeys), 1 << 15, CallFlags.ReadStates, false); public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, CallFlags.WriteStates, false); public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, CallFlags.WriteStates, false); public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 0, CallFlags.WriteStates, false); @@ -63,11 +65,19 @@ protected internal byte[] Get(StorageContext context, byte[] key) protected internal IIterator Find(StorageContext context, byte[] prefix) { byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); - StorageIterator iterator = new StorageIterator(Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.AsSpan().StartsWith(prefix)).GetEnumerator()); + StorageIterator iterator = new StorageIterator(Snapshot.Storages.Find(prefix_key).GetEnumerator()); Disposables.Add(iterator); return iterator; } + protected internal IEnumerator FindKeys(StorageContext context, byte[] prefix, byte removePrefix) + { + byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); + StorageKeyEnumerator enumerator = new StorageKeyEnumerator(Snapshot.Storages.Find(prefix_key).Select(p => p.Key).GetEnumerator(), removePrefix); + Disposables.Add(enumerator); + return enumerator; + } + protected internal void Put(StorageContext context, byte[] key, byte[] value) { PutExInternal(context, key, value, StorageFlags.None); diff --git a/src/neo/SmartContract/Enumerators/StorageKeyEnumerator.cs b/src/neo/SmartContract/Enumerators/StorageKeyEnumerator.cs new file mode 100644 index 0000000000..3bb5b4e9db --- /dev/null +++ b/src/neo/SmartContract/Enumerators/StorageKeyEnumerator.cs @@ -0,0 +1,36 @@ +using Neo.Ledger; +using Neo.VM.Types; +using System.Collections.Generic; + +namespace Neo.SmartContract.Enumerators +{ + internal class StorageKeyEnumerator : IEnumerator + { + private readonly IEnumerator enumerator; + private readonly byte removePrefix; + + public StorageKeyEnumerator(IEnumerator enumerator, byte removePrefix) + { + this.enumerator = enumerator; + this.removePrefix = removePrefix; + } + + public void Dispose() + { + enumerator.Dispose(); + } + + public bool Next() + { + return enumerator.MoveNext(); + } + + public StackItem Value() + { + byte[] key = enumerator.Current.Key; + if (removePrefix > 0) + key = key[removePrefix..]; + return key; + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/Enumerators/UT_StorageKeyEnumerator.cs b/tests/neo.UnitTests/SmartContract/Enumerators/UT_StorageKeyEnumerator.cs new file mode 100644 index 0000000000..6a69e8638c --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/Enumerators/UT_StorageKeyEnumerator.cs @@ -0,0 +1,69 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Ledger; +using Neo.SmartContract.Enumerators; +using System; +using System.Collections.Generic; + +namespace Neo.UnitTests.SmartContract.Enumerators +{ + [TestClass] + public class UT_StorageKeyEnumerator + { + private class TestEnumeratorDispose : IEnumerator + { + public bool IsDisposed { get; private set; } + public StorageKey Current => throw new NotImplementedException(); + object System.Collections.IEnumerator.Current => throw new NotImplementedException(); + public void Dispose() + { + IsDisposed = true; + } + public bool MoveNext() => throw new NotImplementedException(); + public void Reset() => throw new NotImplementedException(); + } + + [TestMethod] + public void TestGeneratorAndDispose() + { + var enumerator = new TestEnumeratorDispose(); + var iterator = new StorageKeyEnumerator(enumerator, 0); + Action action = () => iterator.Dispose(); + enumerator.IsDisposed.Should().BeFalse(); + action.Should().NotThrow(); + enumerator.IsDisposed.Should().BeTrue(); + } + + [TestMethod] + public void TestNextAndValue() + { + var list = new List + { + new StorageKey() { Id = 1, Key = new byte[] { 1, 2, 3 } }, + new StorageKey() { Id = 1, Key = new byte[] { 4, 5, 6 } } + }; + + // With prefix + + var iterator = new StorageKeyEnumerator(list.GetEnumerator(), 0); + Action actionTrue = () => iterator.Next().Should().BeTrue(); + actionTrue.Should().NotThrow(); + CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, iterator.Value().GetSpan().ToArray()); + actionTrue.Should().NotThrow(); + CollectionAssert.AreEqual(new byte[] { 4, 5, 6 }, iterator.Value().GetSpan().ToArray()); + Action actionFalse = () => iterator.Next().Should().BeFalse(); + actionFalse.Should().NotThrow(); + + // Without prefix + + iterator = new StorageKeyEnumerator(list.GetEnumerator(), 2); + actionTrue = () => iterator.Next().Should().BeTrue(); + actionTrue.Should().NotThrow(); + CollectionAssert.AreEqual(new byte[] { 3 }, iterator.Value().GetSpan().ToArray()); + actionTrue.Should().NotThrow(); + CollectionAssert.AreEqual(new byte[] { 6 }, iterator.Value().GetSpan().ToArray()); + actionFalse = () => iterator.Next().Should().BeFalse(); + actionFalse.Should().NotThrow(); + } + } +} From c9df7a463ebde78a991721f253301de11171c607 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 22 Dec 2020 10:42:05 +0800 Subject: [PATCH 02/65] Remove Concat APIs (#2170) --- .../ApplicationEngine.Enumerator.cs | 6 -- .../ApplicationEngine.Iterator.cs | 6 -- .../Enumerators/ConcatenatedEnumerator.cs | 34 -------- .../Iterators/ConcatenatedIterator.cs | 34 -------- .../Enumerators/UT_ConcatenatedEnumerator.cs | 54 ------------- .../Iterators/UT_ConcatenatedIterator.cs | 81 ------------------- .../SmartContract/UT_InteropService.NEO.cs | 38 --------- 7 files changed, 253 deletions(-) delete mode 100644 src/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs delete mode 100644 src/neo/SmartContract/Iterators/ConcatenatedIterator.cs delete mode 100644 tests/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs delete mode 100644 tests/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs diff --git a/src/neo/SmartContract/ApplicationEngine.Enumerator.cs b/src/neo/SmartContract/ApplicationEngine.Enumerator.cs index e680b3cd53..f90a0d6de7 100644 --- a/src/neo/SmartContract/ApplicationEngine.Enumerator.cs +++ b/src/neo/SmartContract/ApplicationEngine.Enumerator.cs @@ -11,7 +11,6 @@ partial class ApplicationEngine public static readonly InteropDescriptor System_Enumerator_Create = Register("System.Enumerator.Create", nameof(CreateEnumerator), 1 << 4, CallFlags.None, false); public static readonly InteropDescriptor System_Enumerator_Next = Register("System.Enumerator.Next", nameof(EnumeratorNext), 1 << 15, CallFlags.None, false); public static readonly InteropDescriptor System_Enumerator_Value = Register("System.Enumerator.Value", nameof(EnumeratorValue), 1 << 4, CallFlags.None, false); - public static readonly InteropDescriptor System_Enumerator_Concat = Register("System.Enumerator.Concat", nameof(ConcatEnumerators), 1 << 4, CallFlags.None, false); protected internal IEnumerator CreateEnumerator(StackItem item) { @@ -33,10 +32,5 @@ protected internal StackItem EnumeratorValue(IEnumerator enumerator) { return enumerator.Value(); } - - protected internal IEnumerator ConcatEnumerators(IEnumerator first, IEnumerator second) - { - return new ConcatenatedEnumerator(first, second); - } } } diff --git a/src/neo/SmartContract/ApplicationEngine.Iterator.cs b/src/neo/SmartContract/ApplicationEngine.Iterator.cs index 6cadaae3b2..92a3f27d2e 100644 --- a/src/neo/SmartContract/ApplicationEngine.Iterator.cs +++ b/src/neo/SmartContract/ApplicationEngine.Iterator.cs @@ -12,7 +12,6 @@ partial class ApplicationEngine public static readonly InteropDescriptor System_Iterator_Key = Register("System.Iterator.Key", nameof(IteratorKey), 1 << 4, CallFlags.None, false); public static readonly InteropDescriptor System_Iterator_Keys = Register("System.Iterator.Keys", nameof(IteratorKeys), 1 << 4, CallFlags.None, false); public static readonly InteropDescriptor System_Iterator_Values = Register("System.Iterator.Values", nameof(IteratorValues), 1 << 4, CallFlags.None, false); - public static readonly InteropDescriptor System_Iterator_Concat = Register("System.Iterator.Concat", nameof(ConcatIterators), 1 << 4, CallFlags.None, false); protected internal IIterator CreateIterator(StackItem item) { @@ -40,10 +39,5 @@ protected internal IEnumerator IteratorValues(IIterator iterator) { return new IteratorValuesWrapper(iterator); } - - protected internal IIterator ConcatIterators(IIterator first, IIterator second) - { - return new ConcatenatedIterator(first, second); - } } } diff --git a/src/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs b/src/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs deleted file mode 100644 index 05cde03e54..0000000000 --- a/src/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Neo.VM.Types; - -namespace Neo.SmartContract.Enumerators -{ - internal class ConcatenatedEnumerator : IEnumerator - { - private readonly IEnumerator first, second; - private IEnumerator current; - - public ConcatenatedEnumerator(IEnumerator first, IEnumerator second) - { - this.current = this.first = first; - this.second = second; - } - - public void Dispose() - { - first.Dispose(); - second.Dispose(); - } - - public bool Next() - { - if (current.Next()) return true; - current = second; - return current.Next(); - } - - public StackItem Value() - { - return current.Value(); - } - } -} diff --git a/src/neo/SmartContract/Iterators/ConcatenatedIterator.cs b/src/neo/SmartContract/Iterators/ConcatenatedIterator.cs deleted file mode 100644 index d8c556f140..0000000000 --- a/src/neo/SmartContract/Iterators/ConcatenatedIterator.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Neo.VM.Types; - -namespace Neo.SmartContract.Iterators -{ - internal class ConcatenatedIterator : IIterator - { - private readonly IIterator first, second; - private IIterator current; - - public ConcatenatedIterator(IIterator first, IIterator second) - { - this.current = this.first = first; - this.second = second; - } - - public PrimitiveType Key() => current.Key(); - public StackItem Value() => current.Value(); - - public bool Next() - { - if (current.Next()) return true; - - current = second; - return current.Next(); - } - - - public void Dispose() - { - first.Dispose(); - second.Dispose(); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs b/tests/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs deleted file mode 100644 index f7ad3a7274..0000000000 --- a/tests/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs +++ /dev/null @@ -1,54 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract.Enumerators; -using Neo.SmartContract.Iterators; -using Neo.VM.Types; -using System; -using System.Collections.Generic; - -namespace Neo.UnitTests.SmartContract.Enumerators -{ - [TestClass] - public class UT_ConcatenatedEnumerator - { - [TestMethod] - public void TestConcatenatedIteratorAndDispose() - { - List list1 = new List(); - StackItem stackItem1 = new Integer(0); - list1.Add(stackItem1); - List list2 = new List(); - StackItem stackItem2 = new Integer(0); - list2.Add(stackItem2); - ArrayWrapper arrayWrapper1 = new ArrayWrapper(list1); - ArrayWrapper arrayWrapper2 = new ArrayWrapper(list2); - IteratorKeysWrapper it1 = new IteratorKeysWrapper(arrayWrapper1); - IteratorKeysWrapper it2 = new IteratorKeysWrapper(arrayWrapper2); - ConcatenatedEnumerator uut = new ConcatenatedEnumerator(it1, it2); - Assert.IsNotNull(uut); - Action action = () => uut.Dispose(); - action.Should().NotThrow(); - } - - [TestMethod] - public void TestNextAndValue() - { - List list1 = new List(); - StackItem stackItem1 = new Integer(1); - list1.Add(stackItem1); - List list2 = new List(); - StackItem stackItem2 = new Integer(0); - list2.Add(stackItem2); - ArrayWrapper arrayWrapper1 = new ArrayWrapper(list1); - ArrayWrapper arrayWrapper2 = new ArrayWrapper(list2); - IteratorKeysWrapper it1 = new IteratorKeysWrapper(arrayWrapper1); - IteratorKeysWrapper it2 = new IteratorKeysWrapper(arrayWrapper2); - ConcatenatedEnumerator uut = new ConcatenatedEnumerator(it1, it2); - Assert.AreEqual(true, uut.Next()); - Assert.AreEqual(new Integer(0), uut.Value()); - Assert.AreEqual(true, uut.Next()); - Assert.AreEqual(new Integer(0), uut.Value()); - Assert.AreEqual(false, uut.Next()); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs deleted file mode 100644 index 84b3ffe041..0000000000 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs +++ /dev/null @@ -1,81 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract.Iterators; -using Neo.VM.Types; -using System; -using System.Numerics; - -namespace Neo.UnitTests.SmartContract.Iterators -{ - [TestClass] - public class UT_ConcatenatedIterator - { - [TestMethod] - public void ConcatenatedIteratedOverflowTest() - { - Integer[] array1 = { MakeIntegerStackItem(1) }; - ArrayWrapper it1 = new ArrayWrapper(array1); - ArrayWrapper it2 = new ArrayWrapper(array1); - ConcatenatedIterator uut = new ConcatenatedIterator(it1, it2); - - uut.Next().Should().Be(true); - uut.Key().Should().Be(MakeIntegerStackItem(0)); - uut.Value().Should().Be(array1[0]); - - uut.Next().Should().Be(true); - uut.Key().Should().Be(MakeIntegerStackItem(0)); - uut.Value().Should().Be(array1[0]); - - uut.Next().Should().Be(false); - } - - [TestMethod] - public void ConcatenatedIteratedTest() - { - Integer[] array1 = { MakeIntegerStackItem(1), MakeIntegerStackItem(7), MakeIntegerStackItem(23) }; - Integer[] array2 = { MakeIntegerStackItem(8), MakeIntegerStackItem(47) }; - ArrayWrapper it1 = new ArrayWrapper(array1); - ArrayWrapper it2 = new ArrayWrapper(array2); - ConcatenatedIterator uut = new ConcatenatedIterator(it1, it2); - - uut.Next().Should().Be(true); - uut.Key().Should().Be(MakeIntegerStackItem(0)); - uut.Value().Should().Be(array1[0]); - - uut.Next().Should().Be(true); - uut.Key().Should().Be(MakeIntegerStackItem(1)); - uut.Value().Should().Be(array1[1]); - - uut.Next().Should().Be(true); - uut.Key().Should().Be(MakeIntegerStackItem(2)); - uut.Value().Should().Be(array1[2]); - - uut.Next().Should().Be(true); - uut.Key().Should().Be(MakeIntegerStackItem(0)); - uut.Value().Should().Be(array2[0]); - - uut.Next().Should().Be(true); - uut.Key().Should().Be(MakeIntegerStackItem(1)); - uut.Value().Should().Be(array2[1]); - - uut.Next().Should().Be(false); - } - - private Integer MakeIntegerStackItem(int val) - { - return new Integer(new BigInteger(val)); - } - - [TestMethod] - public void TestDispose() - { - Integer[] array1 = { MakeIntegerStackItem(1), MakeIntegerStackItem(7), MakeIntegerStackItem(23) }; - Integer[] array2 = { MakeIntegerStackItem(8), MakeIntegerStackItem(47) }; - ArrayWrapper it1 = new ArrayWrapper(array1); - ArrayWrapper it2 = new ArrayWrapper(array2); - ConcatenatedIterator uut = new ConcatenatedIterator(it1, it2); - Action action = () => uut.Dispose(); - action.Should().NotThrow(); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index b674c10a62..09ab10c78b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -323,25 +323,6 @@ public void TestEnumerator_Value() engine.EnumeratorValue(wrapper).GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } - [TestMethod] - public void TestEnumerator_Concat() - { - var engine = GetEngine(); - var arr1 = new VMArray { - new byte[]{ 0x01 }, - new byte[]{ 0x02 } - }; - var arr2 = new VMArray { - new byte[]{ 0x03 }, - new byte[]{ 0x04 } - }; - var wrapper1 = new ArrayWrapper(arr1); - var wrapper2 = new ArrayWrapper(arr2); - var ret = engine.ConcatEnumerators(wrapper1, wrapper2); - ret.Next().Should().BeTrue(); - ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); - } - [TestMethod] public void TestIterator_Create() { @@ -409,25 +390,6 @@ public void TestIterator_Values() ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } - [TestMethod] - public void TestIterator_Concat() - { - var engine = GetEngine(); - var arr1 = new VMArray { - new byte[]{ 0x01 }, - new byte[]{ 0x02 } - }; - var arr2 = new VMArray { - new byte[]{ 0x03 }, - new byte[]{ 0x04 } - }; - var wrapper1 = new ArrayWrapper(arr1); - var wrapper2 = new ArrayWrapper(arr2); - var ret = engine.ConcatIterators(wrapper1, wrapper2); - ret.Next().Should().BeTrue(); - ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); - } - [TestMethod] public void TestJson_Deserialize() { From 095d7f7f5c5cad070f35f48e37bb189c5bb2a511 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 22 Dec 2020 10:45:07 +0800 Subject: [PATCH 03/65] Allow to load store from a specified path (#2172) --- src/neo/NeoSystem.cs | 15 +++++++++++---- src/neo/Plugins/IStorageProvider.cs | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 88cc93ebcd..76980ca319 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -22,6 +22,7 @@ public class NeoSystem : IDisposable internal IActorRef TaskManager { get; } public IActorRef Consensus { get; private set; } + private readonly string storage_engine; private readonly IStore store; private ChannelsConfig start_message = null; private bool suspend = false; @@ -32,12 +33,11 @@ static NeoSystem() AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; } - public NeoSystem(string storageEngine = null) + public NeoSystem(string storageEngine = null, string storagePath = null) { Plugin.LoadPlugins(this); - this.store = string.IsNullOrEmpty(storageEngine) || storageEngine == nameof(MemoryStore) - ? new MemoryStore() - : Plugin.Storages[storageEngine].GetStore(); + this.storage_engine = storageEngine; + this.store = LoadStore(storagePath); this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); @@ -69,6 +69,13 @@ public void EnsureStoped(IActorRef actor) inbox.Receive(TimeSpan.FromMinutes(5)); } + public IStore LoadStore(string path) + { + return string.IsNullOrEmpty(storage_engine) || storage_engine == nameof(MemoryStore) + ? new MemoryStore() + : Plugin.Storages[storage_engine].GetStore(path); + } + internal void ResumeNodeStartup() { suspend = false; diff --git a/src/neo/Plugins/IStorageProvider.cs b/src/neo/Plugins/IStorageProvider.cs index 5ef2d9f098..b8b7197b6c 100644 --- a/src/neo/Plugins/IStorageProvider.cs +++ b/src/neo/Plugins/IStorageProvider.cs @@ -4,6 +4,6 @@ namespace Neo.Plugins { public interface IStorageProvider { - IStore GetStore(); + IStore GetStore(string path); } } From d970bb991bab93ba692c7b54abc1dc74f6f2073e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 24 Dec 2020 22:45:14 +0800 Subject: [PATCH 04/65] Remove Callback APIs (#2168) --- .../SmartContract/ApplicationEngine.Binary.cs | 16 ++-- .../ApplicationEngine.Blockchain.cs | 10 +- .../ApplicationEngine.Callback.cs | 39 -------- .../ApplicationEngine.Contract.cs | 14 +-- .../SmartContract/ApplicationEngine.Crypto.cs | 12 +-- .../ApplicationEngine.Enumerator.cs | 6 +- .../ApplicationEngine.Iterator.cs | 8 +- .../SmartContract/ApplicationEngine.Json.cs | 4 +- .../ApplicationEngine.Runtime.cs | 26 +++--- .../ApplicationEngine.Storage.cs | 18 ++-- src/neo/SmartContract/ApplicationEngine.cs | 13 +-- .../SmartContract/Callbacks/CallbackBase.cs | 11 --- .../SmartContract/Callbacks/MethodCallback.cs | 36 ------- .../Callbacks/PointerCallback.cs | 27 ------ .../Callbacks/SyscallCallback.cs | 24 ----- src/neo/SmartContract/InteropDescriptor.cs | 4 +- .../Callbacks/UT_MethodCallback.cs | 68 -------------- .../SmartContract/UT_Syscalls.Callback.cs | 93 ------------------- 18 files changed, 61 insertions(+), 368 deletions(-) delete mode 100644 src/neo/SmartContract/ApplicationEngine.Callback.cs delete mode 100644 src/neo/SmartContract/Callbacks/CallbackBase.cs delete mode 100644 src/neo/SmartContract/Callbacks/MethodCallback.cs delete mode 100644 src/neo/SmartContract/Callbacks/PointerCallback.cs delete mode 100644 src/neo/SmartContract/Callbacks/SyscallCallback.cs delete mode 100644 tests/neo.UnitTests/SmartContract/Callbacks/UT_MethodCallback.cs delete mode 100644 tests/neo.UnitTests/SmartContract/UT_Syscalls.Callback.cs diff --git a/src/neo/SmartContract/ApplicationEngine.Binary.cs b/src/neo/SmartContract/ApplicationEngine.Binary.cs index d8a3c53b47..cfc8197bd6 100644 --- a/src/neo/SmartContract/ApplicationEngine.Binary.cs +++ b/src/neo/SmartContract/ApplicationEngine.Binary.cs @@ -9,14 +9,14 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Binary_Serialize = Register("System.Binary.Serialize", nameof(BinarySerialize), 1 << 12, CallFlags.None, true); - public static readonly InteropDescriptor System_Binary_Deserialize = Register("System.Binary.Deserialize", nameof(BinaryDeserialize), 1 << 14, CallFlags.None, true); - public static readonly InteropDescriptor System_Binary_Base64Encode = Register("System.Binary.Base64Encode", nameof(Base64Encode), 1 << 12, CallFlags.None, true); - public static readonly InteropDescriptor System_Binary_Base64Decode = Register("System.Binary.Base64Decode", nameof(Base64Decode), 1 << 12, CallFlags.None, true); - public static readonly InteropDescriptor System_Binary_Base58Encode = Register("System.Binary.Base58Encode", nameof(Base58Encode), 1 << 12, CallFlags.None, true); - public static readonly InteropDescriptor System_Binary_Base58Decode = Register("System.Binary.Base58Decode", nameof(Base58Decode), 1 << 12, CallFlags.None, true); - public static readonly InteropDescriptor System_Binary_Itoa = Register("System.Binary.Itoa", nameof(Itoa), 1 << 12, CallFlags.None, true); - public static readonly InteropDescriptor System_Binary_Atoi = Register("System.Binary.Atoi", nameof(Atoi), 1 << 12, CallFlags.None, true); + public static readonly InteropDescriptor System_Binary_Serialize = Register("System.Binary.Serialize", nameof(BinarySerialize), 1 << 12, CallFlags.None); + public static readonly InteropDescriptor System_Binary_Deserialize = Register("System.Binary.Deserialize", nameof(BinaryDeserialize), 1 << 14, CallFlags.None); + public static readonly InteropDescriptor System_Binary_Base64Encode = Register("System.Binary.Base64Encode", nameof(Base64Encode), 1 << 12, CallFlags.None); + public static readonly InteropDescriptor System_Binary_Base64Decode = Register("System.Binary.Base64Decode", nameof(Base64Decode), 1 << 12, CallFlags.None); + public static readonly InteropDescriptor System_Binary_Base58Encode = Register("System.Binary.Base58Encode", nameof(Base58Encode), 1 << 12, CallFlags.None); + public static readonly InteropDescriptor System_Binary_Base58Decode = Register("System.Binary.Base58Decode", nameof(Base58Decode), 1 << 12, CallFlags.None); + public static readonly InteropDescriptor System_Binary_Itoa = Register("System.Binary.Itoa", nameof(Itoa), 1 << 12, CallFlags.None); + public static readonly InteropDescriptor System_Binary_Atoi = Register("System.Binary.Atoi", nameof(Atoi), 1 << 12, CallFlags.None); protected internal byte[] BinarySerialize(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Blockchain.cs b/src/neo/SmartContract/ApplicationEngine.Blockchain.cs index 0b7fb2f9dd..fef191fb44 100644 --- a/src/neo/SmartContract/ApplicationEngine.Blockchain.cs +++ b/src/neo/SmartContract/ApplicationEngine.Blockchain.cs @@ -8,11 +8,11 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", nameof(GetBlockchainHeight), 1 << 4, CallFlags.ReadStates, true); - public static readonly InteropDescriptor System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", nameof(GetBlock), 1 << 16, CallFlags.ReadStates, true); - public static readonly InteropDescriptor System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", nameof(GetTransaction), 1 << 15, CallFlags.ReadStates, true); - public static readonly InteropDescriptor System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", nameof(GetTransactionHeight), 1 << 15, CallFlags.ReadStates, true); - public static readonly InteropDescriptor System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", nameof(GetTransactionFromBlock), 1 << 15, CallFlags.ReadStates, true); + public static readonly InteropDescriptor System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", nameof(GetBlockchainHeight), 1 << 4, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", nameof(GetBlock), 1 << 16, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", nameof(GetTransaction), 1 << 15, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", nameof(GetTransactionHeight), 1 << 15, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", nameof(GetTransactionFromBlock), 1 << 15, CallFlags.ReadStates); protected internal uint GetBlockchainHeight() { diff --git a/src/neo/SmartContract/ApplicationEngine.Callback.cs b/src/neo/SmartContract/ApplicationEngine.Callback.cs deleted file mode 100644 index efa3804183..0000000000 --- a/src/neo/SmartContract/ApplicationEngine.Callback.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Neo.SmartContract.Callbacks; -using Neo.VM.Types; -using System; -using Array = Neo.VM.Types.Array; - -namespace Neo.SmartContract -{ - partial class ApplicationEngine - { - public static readonly InteropDescriptor System_Callback_Create = Register("System.Callback.Create", nameof(CreateCallback), 1 << 4, CallFlags.None, false); - public static readonly InteropDescriptor System_Callback_CreateFromMethod = Register("System.Callback.CreateFromMethod", nameof(CreateCallbackFromMethod), 1 << 15, CallFlags.ReadStates, false); - public static readonly InteropDescriptor System_Callback_CreateFromSyscall = Register("System.Callback.CreateFromSyscall", nameof(CreateCallbackFromSyscall), 1 << 4, CallFlags.None, false); - public static readonly InteropDescriptor System_Callback_Invoke = Register("System.Callback.Invoke", nameof(InvokeCallback), 1 << 15, CallFlags.AllowCall, false); - - protected internal void InvokeCallback(CallbackBase callback, Array args) - { - if (args.Count != callback.ParametersCount) - throw new InvalidOperationException(); - callback.LoadContext(this, args); - if (callback is SyscallCallback syscallCallback) - OnSysCall(syscallCallback.Method); - } - - protected internal PointerCallback CreateCallback(Pointer pointer, int parcount) - { - return new PointerCallback(CurrentContext, pointer, parcount); - } - - protected internal MethodCallback CreateCallbackFromMethod(UInt160 hash, string method) - { - return new MethodCallback(this, hash, method); - } - - protected internal SyscallCallback CreateCallbackFromSyscall(uint method) - { - return new SyscallCallback(method); - } - } -} diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 78133563f2..7d43efe74e 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -10,17 +10,17 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Contract_CallEx = Register("System.Contract.CallEx", nameof(CallContractEx), 1 << 15, CallFlags.AllowCall, false); - public static readonly InteropDescriptor System_Contract_CallNative = Register("System.Contract.CallNative", nameof(CallNativeContract), 0, CallFlags.None, false); - public static readonly InteropDescriptor System_Contract_IsStandard = Register("System.Contract.IsStandard", nameof(IsStandardContract), 1 << 10, CallFlags.ReadStates, true); - public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 1 << 10, CallFlags.None, false); + public static readonly InteropDescriptor System_Contract_CallEx = Register("System.Contract.CallEx", nameof(CallContractEx), 1 << 15, CallFlags.AllowCall); + public static readonly InteropDescriptor System_Contract_CallNative = Register("System.Contract.CallNative", nameof(CallNativeContract), 0, CallFlags.None); + public static readonly InteropDescriptor System_Contract_IsStandard = Register("System.Contract.IsStandard", nameof(IsStandardContract), 1 << 10, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 1 << 10, CallFlags.None); /// /// Calculate corresponding account scripthash for given public key /// Warning: check first that input public key is valid, before creating the script. /// - public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 1 << 8, CallFlags.None, true); - public static readonly InteropDescriptor System_Contract_NativeOnPersist = Register("System.Contract.NativeOnPersist", nameof(NativeOnPersist), 0, CallFlags.WriteStates, false); - public static readonly InteropDescriptor System_Contract_NativePostPersist = Register("System.Contract.NativePostPersist", nameof(NativePostPersist), 0, CallFlags.WriteStates, false); + public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 1 << 8, CallFlags.None); + public static readonly InteropDescriptor System_Contract_NativeOnPersist = Register("System.Contract.NativeOnPersist", nameof(NativeOnPersist), 0, CallFlags.WriteStates); + public static readonly InteropDescriptor System_Contract_NativePostPersist = Register("System.Contract.NativePostPersist", nameof(NativePostPersist), 0, CallFlags.WriteStates); protected internal void CallContractEx(UInt160 contractHash, string method, Array args, CallFlags callFlags) { diff --git a/src/neo/SmartContract/ApplicationEngine.Crypto.cs b/src/neo/SmartContract/ApplicationEngine.Crypto.cs index 798dd0556b..e3988534cf 100644 --- a/src/neo/SmartContract/ApplicationEngine.Crypto.cs +++ b/src/neo/SmartContract/ApplicationEngine.Crypto.cs @@ -11,12 +11,12 @@ partial class ApplicationEngine { public const long ECDsaVerifyPrice = 1 << 15; - public static readonly InteropDescriptor Neo_Crypto_RIPEMD160 = Register("Neo.Crypto.RIPEMD160", nameof(RIPEMD160), 1 << 15, CallFlags.None, true); - public static readonly InteropDescriptor Neo_Crypto_SHA256 = Register("Neo.Crypto.SHA256", nameof(Sha256), 1 << 15, CallFlags.None, true); - public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.VerifyWithECDsaSecp256r1", nameof(VerifyWithECDsaSecp256r1), ECDsaVerifyPrice, CallFlags.None, true); - public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.VerifyWithECDsaSecp256k1", nameof(VerifyWithECDsaSecp256k1), ECDsaVerifyPrice, CallFlags.None, true); - public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256r1", nameof(CheckMultisigWithECDsaSecp256r1), 0, CallFlags.None, true); - public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256k1", nameof(CheckMultisigWithECDsaSecp256k1), 0, CallFlags.None, true); + public static readonly InteropDescriptor Neo_Crypto_RIPEMD160 = Register("Neo.Crypto.RIPEMD160", nameof(RIPEMD160), 1 << 15, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_SHA256 = Register("Neo.Crypto.SHA256", nameof(Sha256), 1 << 15, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.VerifyWithECDsaSecp256r1", nameof(VerifyWithECDsaSecp256r1), ECDsaVerifyPrice, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.VerifyWithECDsaSecp256k1", nameof(VerifyWithECDsaSecp256k1), ECDsaVerifyPrice, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256r1", nameof(CheckMultisigWithECDsaSecp256r1), 0, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256k1", nameof(CheckMultisigWithECDsaSecp256k1), 0, CallFlags.None); protected internal byte[] RIPEMD160(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Enumerator.cs b/src/neo/SmartContract/ApplicationEngine.Enumerator.cs index f90a0d6de7..f0e5839651 100644 --- a/src/neo/SmartContract/ApplicationEngine.Enumerator.cs +++ b/src/neo/SmartContract/ApplicationEngine.Enumerator.cs @@ -8,9 +8,9 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Enumerator_Create = Register("System.Enumerator.Create", nameof(CreateEnumerator), 1 << 4, CallFlags.None, false); - public static readonly InteropDescriptor System_Enumerator_Next = Register("System.Enumerator.Next", nameof(EnumeratorNext), 1 << 15, CallFlags.None, false); - public static readonly InteropDescriptor System_Enumerator_Value = Register("System.Enumerator.Value", nameof(EnumeratorValue), 1 << 4, CallFlags.None, false); + public static readonly InteropDescriptor System_Enumerator_Create = Register("System.Enumerator.Create", nameof(CreateEnumerator), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Enumerator_Next = Register("System.Enumerator.Next", nameof(EnumeratorNext), 1 << 15, CallFlags.None); + public static readonly InteropDescriptor System_Enumerator_Value = Register("System.Enumerator.Value", nameof(EnumeratorValue), 1 << 4, CallFlags.None); protected internal IEnumerator CreateEnumerator(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Iterator.cs b/src/neo/SmartContract/ApplicationEngine.Iterator.cs index 92a3f27d2e..685d195f2a 100644 --- a/src/neo/SmartContract/ApplicationEngine.Iterator.cs +++ b/src/neo/SmartContract/ApplicationEngine.Iterator.cs @@ -8,10 +8,10 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Iterator_Create = Register("System.Iterator.Create", nameof(CreateIterator), 1 << 4, CallFlags.None, false); - public static readonly InteropDescriptor System_Iterator_Key = Register("System.Iterator.Key", nameof(IteratorKey), 1 << 4, CallFlags.None, false); - public static readonly InteropDescriptor System_Iterator_Keys = Register("System.Iterator.Keys", nameof(IteratorKeys), 1 << 4, CallFlags.None, false); - public static readonly InteropDescriptor System_Iterator_Values = Register("System.Iterator.Values", nameof(IteratorValues), 1 << 4, CallFlags.None, false); + public static readonly InteropDescriptor System_Iterator_Create = Register("System.Iterator.Create", nameof(CreateIterator), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Key = Register("System.Iterator.Key", nameof(IteratorKey), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Keys = Register("System.Iterator.Keys", nameof(IteratorKeys), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Values = Register("System.Iterator.Values", nameof(IteratorValues), 1 << 4, CallFlags.None); protected internal IIterator CreateIterator(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Json.cs b/src/neo/SmartContract/ApplicationEngine.Json.cs index e09f3f150f..40676ed580 100644 --- a/src/neo/SmartContract/ApplicationEngine.Json.cs +++ b/src/neo/SmartContract/ApplicationEngine.Json.cs @@ -5,8 +5,8 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Json_Serialize = Register("System.Json.Serialize", nameof(JsonSerialize), 1 << 12, CallFlags.None, true); - public static readonly InteropDescriptor System_Json_Deserialize = Register("System.Json.Deserialize", nameof(JsonDeserialize), 1 << 14, CallFlags.None, true); + public static readonly InteropDescriptor System_Json_Serialize = Register("System.Json.Serialize", nameof(JsonSerialize), 1 << 12, CallFlags.None); + public static readonly InteropDescriptor System_Json_Deserialize = Register("System.Json.Deserialize", nameof(JsonDeserialize), 1 << 14, CallFlags.None); protected internal byte[] JsonSerialize(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index 2c1c2243fd..601af69037 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -15,19 +15,19 @@ partial class ApplicationEngine public const int MaxEventName = 32; public const int MaxNotificationSize = 1024; - public static readonly InteropDescriptor System_Runtime_Platform = Register("System.Runtime.Platform", nameof(GetPlatform), 1 << 3, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", nameof(Trigger), 1 << 3, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_GetTime = Register("System.Runtime.GetTime", nameof(GetTime), 1 << 3, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_GetScriptContainer = Register("System.Runtime.GetScriptContainer", nameof(GetScriptContainer), 1 << 3, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", nameof(CurrentScriptHash), 1 << 4, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", nameof(CallingScriptHash), 1 << 4, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", nameof(EntryScriptHash), 1 << 4, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", nameof(CheckWitness), 1 << 10, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", nameof(GetInvocationCounter), 1 << 4, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_Log = Register("System.Runtime.Log", nameof(RuntimeLog), 1 << 15, CallFlags.AllowNotify, false); - public static readonly InteropDescriptor System_Runtime_Notify = Register("System.Runtime.Notify", nameof(RuntimeNotify), 1 << 15, CallFlags.AllowNotify, false); - public static readonly InteropDescriptor System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", nameof(GetNotifications), 1 << 8, CallFlags.None, true); - public static readonly InteropDescriptor System_Runtime_GasLeft = Register("System.Runtime.GasLeft", nameof(GasLeft), 1 << 4, CallFlags.None, true); + public static readonly InteropDescriptor System_Runtime_Platform = Register("System.Runtime.Platform", nameof(GetPlatform), 1 << 3, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", nameof(Trigger), 1 << 3, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetTime = Register("System.Runtime.GetTime", nameof(GetTime), 1 << 3, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetScriptContainer = Register("System.Runtime.GetScriptContainer", nameof(GetScriptContainer), 1 << 3, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", nameof(CurrentScriptHash), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", nameof(CallingScriptHash), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", nameof(EntryScriptHash), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", nameof(CheckWitness), 1 << 10, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", nameof(GetInvocationCounter), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_Log = Register("System.Runtime.Log", nameof(RuntimeLog), 1 << 15, CallFlags.AllowNotify); + public static readonly InteropDescriptor System_Runtime_Notify = Register("System.Runtime.Notify", nameof(RuntimeNotify), 1 << 15, CallFlags.AllowNotify); + public static readonly InteropDescriptor System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", nameof(GetNotifications), 1 << 8, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GasLeft = Register("System.Runtime.GasLeft", nameof(GasLeft), 1 << 4, CallFlags.None); protected internal string GetPlatform() { diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index f11ddd2187..2d542f7501 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -12,15 +12,15 @@ partial class ApplicationEngine public const int MaxStorageKeySize = 64; public const int MaxStorageValueSize = ushort.MaxValue; - public static readonly InteropDescriptor System_Storage_GetContext = Register("System.Storage.GetContext", nameof(GetStorageContext), 1 << 4, CallFlags.ReadStates, false); - public static readonly InteropDescriptor System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", nameof(GetReadOnlyContext), 1 << 4, CallFlags.ReadStates, false); - public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 1 << 4, CallFlags.ReadStates, false); - public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 1 << 15, CallFlags.ReadStates, false); - public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 1 << 15, CallFlags.ReadStates, false); - public static readonly InteropDescriptor System_Storage_FindKeys = Register("System.Storage.FindKeys", nameof(FindKeys), 1 << 15, CallFlags.ReadStates, false); - public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, CallFlags.WriteStates, false); - public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, CallFlags.WriteStates, false); - public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 0, CallFlags.WriteStates, false); + public static readonly InteropDescriptor System_Storage_GetContext = Register("System.Storage.GetContext", nameof(GetStorageContext), 1 << 4, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", nameof(GetReadOnlyContext), 1 << 4, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 1 << 4, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 1 << 15, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 1 << 15, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Storage_FindKeys = Register("System.Storage.FindKeys", nameof(FindKeys), 1 << 15, CallFlags.ReadStates); + public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, CallFlags.WriteStates); + public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, CallFlags.WriteStates); + public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 0, CallFlags.WriteStates); protected internal StorageContext GetStorageContext() { diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 90892e1227..9c02bbab60 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -152,13 +152,6 @@ protected override void LoadContext(ExecutionContext context) base.LoadContext(context); } - internal void LoadContext(ExecutionContext context, bool checkReturnValue) - { - if (checkReturnValue) - GetInvocationState(CurrentContext).Convention = ReturnTypeConvention.EnsureNotEmpty; - LoadContext(context); - } - public ExecutionContext LoadContract(ContractState contract, string method, CallFlags callFlags, bool packParameters = false) { ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); @@ -184,7 +177,7 @@ public ExecutionContext LoadContract(ContractState contract, string method, Call if (init != null) { - LoadContext(context.Clone(init.Offset), false); + LoadContext(context.Clone(init.Offset)); } } @@ -331,11 +324,11 @@ private static Block CreateDummyBlock(StoreView snapshot) }; } - private static InteropDescriptor Register(string name, string handler, long fixedPrice, CallFlags requiredCallFlags, bool allowCallback) + private static InteropDescriptor Register(string name, string handler, long fixedPrice, CallFlags requiredCallFlags) { MethodInfo method = typeof(ApplicationEngine).GetMethod(handler, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) ?? typeof(ApplicationEngine).GetProperty(handler, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetMethod; - InteropDescriptor descriptor = new InteropDescriptor(name, method, fixedPrice, requiredCallFlags, allowCallback); + InteropDescriptor descriptor = new InteropDescriptor(name, method, fixedPrice, requiredCallFlags); services ??= new Dictionary(); services.Add(descriptor.Hash, descriptor); return descriptor; diff --git a/src/neo/SmartContract/Callbacks/CallbackBase.cs b/src/neo/SmartContract/Callbacks/CallbackBase.cs deleted file mode 100644 index 7444e9fa85..0000000000 --- a/src/neo/SmartContract/Callbacks/CallbackBase.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Neo.VM.Types; - -namespace Neo.SmartContract.Callbacks -{ - public abstract class CallbackBase - { - public abstract int ParametersCount { get; } - - public abstract void LoadContext(ApplicationEngine engine, Array args); - } -} diff --git a/src/neo/SmartContract/Callbacks/MethodCallback.cs b/src/neo/SmartContract/Callbacks/MethodCallback.cs deleted file mode 100644 index 7bcc8d5ab4..0000000000 --- a/src/neo/SmartContract/Callbacks/MethodCallback.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Neo.IO; -using Neo.SmartContract.Manifest; -using Neo.SmartContract.Native; -using System; -using System.Linq; -using Array = Neo.VM.Types.Array; - -namespace Neo.SmartContract.Callbacks -{ - public class MethodCallback : SyscallCallback - { - private readonly ContractState contract; - private readonly ContractMethodDescriptor method; - - public override int ParametersCount => method.Parameters.Length; - - public MethodCallback(ApplicationEngine engine, UInt160 hash, string method) - : base(ApplicationEngine.System_Contract_CallEx, false) - { - if (method.StartsWith('_')) throw new ArgumentException(); - this.contract = NativeContract.ContractManagement.GetContract(engine.Snapshot, hash); - ContractState currentContract = NativeContract.ContractManagement.GetContract(engine.Snapshot, engine.CurrentScriptHash); - if (currentContract?.CanCall(this.contract, method) == false) - throw new InvalidOperationException(); - this.method = this.contract.Manifest.Abi.Methods.First(p => p.Name == method); - } - - public override void LoadContext(ApplicationEngine engine, Array args) - { - engine.Push((byte)CallFlags.All); - engine.Push(args); - engine.Push(method.Name); - engine.Push(contract.Hash.ToArray()); - } - } -} diff --git a/src/neo/SmartContract/Callbacks/PointerCallback.cs b/src/neo/SmartContract/Callbacks/PointerCallback.cs deleted file mode 100644 index 6c06cf284b..0000000000 --- a/src/neo/SmartContract/Callbacks/PointerCallback.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Neo.VM; -using Neo.VM.Types; - -namespace Neo.SmartContract.Callbacks -{ - public class PointerCallback : CallbackBase - { - private readonly ExecutionContext context; - private readonly int pointer; - - public override int ParametersCount { get; } - - public PointerCallback(ExecutionContext context, Pointer pointer, int parametersCount) - { - this.context = context; - this.pointer = pointer.Position; - this.ParametersCount = parametersCount; - } - - public override void LoadContext(ApplicationEngine engine, Array args) - { - engine.LoadContext(context.Clone(pointer), true); - for (int i = args.Count - 1; i >= 0; i--) - engine.Push(args[i]); - } - } -} diff --git a/src/neo/SmartContract/Callbacks/SyscallCallback.cs b/src/neo/SmartContract/Callbacks/SyscallCallback.cs deleted file mode 100644 index eab1eb7c67..0000000000 --- a/src/neo/SmartContract/Callbacks/SyscallCallback.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Array = Neo.VM.Types.Array; - -namespace Neo.SmartContract.Callbacks -{ - public class SyscallCallback : CallbackBase - { - public InteropDescriptor Method { get; } - public override int ParametersCount => Method.Parameters.Count; - - public SyscallCallback(uint method, bool check = true) - { - this.Method = ApplicationEngine.Services[method]; - if (check && !Method.AllowCallback) - throw new InvalidOperationException("This SYSCALL is not allowed for creating callback."); - } - - public override void LoadContext(ApplicationEngine engine, Array args) - { - for (int i = args.Count - 1; i >= 0; i--) - engine.Push(args[i]); - } - } -} diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index 9fffefde73..ea7acc2144 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -15,9 +15,8 @@ public class InteropDescriptor public IReadOnlyList Parameters { get; } public long FixedPrice { get; } public CallFlags RequiredCallFlags { get; } - public bool AllowCallback { get; } - internal InteropDescriptor(string name, MethodInfo handler, long fixedPrice, CallFlags requiredCallFlags, bool allowCallback) + internal InteropDescriptor(string name, MethodInfo handler, long fixedPrice, CallFlags requiredCallFlags) { this.Name = name; this.Hash = BitConverter.ToUInt32(Encoding.ASCII.GetBytes(name).Sha256(), 0); @@ -25,7 +24,6 @@ internal InteropDescriptor(string name, MethodInfo handler, long fixedPrice, Cal this.Parameters = handler.GetParameters().Select(p => new InteropParameterDescriptor(p)).ToList().AsReadOnly(); this.FixedPrice = fixedPrice; this.RequiredCallFlags = requiredCallFlags; - this.AllowCallback = allowCallback; } public static implicit operator uint(InteropDescriptor descriptor) diff --git a/tests/neo.UnitTests/SmartContract/Callbacks/UT_MethodCallback.cs b/tests/neo.UnitTests/SmartContract/Callbacks/UT_MethodCallback.cs deleted file mode 100644 index bc39a51725..0000000000 --- a/tests/neo.UnitTests/SmartContract/Callbacks/UT_MethodCallback.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Ledger; -using Neo.SmartContract; -using Neo.SmartContract.Callbacks; -using Neo.SmartContract.Manifest; -using Neo.UnitTests.Extensions; -using System; - -namespace Neo.UnitTests.SmartContract.Callbacks -{ - [TestClass] - public class UT_MethodCallback - { - [TestInitialize] - public void Init() - { - TestBlockchain.InitializeMockNeoSystem(); - } - - [TestMethod] - public void GetHashData() - { - var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - - Assert.ThrowsException(() => new MethodCallback(engine, UInt160.Zero, "_test")); - - var contract = new ContractState() - { - Manifest = new ContractManifest() - { - Permissions = new ContractPermission[0], - Groups = new ContractGroup[0], - Trusts = WildcardContainer.Create(), - Abi = new ContractAbi() - { - Methods = new ContractMethodDescriptor[] - { - new ContractMethodDescriptor(){ Name="test", Parameters=new ContractParameterDefinition[0]} - }, - Events = new ContractEventDescriptor[0], - }, - }, - Script = new byte[] { 1, 2, 3 }, - Hash = new byte[] { 1, 2, 3 }.ToScriptHash() - }; - engine.LoadScript(contract.Script); - engine.Snapshot.AddContract(contract.Hash, contract); - - Assert.ThrowsException(() => new MethodCallback(engine, contract.Hash, "test")); - - contract.Manifest.Permissions = new ContractPermission[] { - new ContractPermission() { Contract = ContractPermissionDescriptor.Create(contract.Hash), - Methods= WildcardContainer.Create("test") } }; - var data = new MethodCallback(engine, contract.Hash, "test"); - - Assert.AreEqual(0, engine.CurrentContext.EvaluationStack.Count); - var array = new VM.Types.Array(); - - data.LoadContext(engine, array); - - Assert.AreEqual(4, engine.CurrentContext.EvaluationStack.Count); - Assert.AreEqual("9bc4860bb936abf262d7a51f74b4304833fee3b2", engine.Pop().GetSpan().ToHexString()); - Assert.AreEqual("test", engine.Pop().GetString()); - Assert.IsTrue(engine.Pop() == array); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.Callback.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.Callback.cs deleted file mode 100644 index 41ddd025e0..0000000000 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.Callback.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract; -using Neo.SmartContract.Callbacks; -using Neo.VM; -using Neo.VM.Types; -using System; - -namespace Neo.UnitTests.SmartContract -{ - public partial class UT_Syscalls - { - [TestMethod] - public void CreateCallbackTest() - { - using var script = new ScriptBuilder(); - - script.EmitPush(5); // Callback argument - script.EmitPush(1); // ParamCount - script.Emit(OpCode.PUSHA, BitConverter.GetBytes(0)); - script.EmitSysCall(ApplicationEngine.System_Callback_Create); - - // Execute - - var engine = ApplicationEngine.Create(TriggerType.Application, null, null, 100_000_000); - engine.LoadScript(script.ToArray()); - Assert.AreEqual(engine.Execute(), VMState.HALT); - - // Check the results - - Assert.AreEqual(2, engine.ResultStack.Count); - var callback = engine.ResultStack.Pop().GetInterface(); - Assert.AreEqual(1, callback.ParametersCount); - } - - [TestMethod] - public void InvokeCallbackTest() - { - using var script = new ScriptBuilder(); - - script.EmitPush(5); // Callback argument 1 - script.EmitPush(1); // Callback argument 2 - script.EmitPush(2); // ParamCount - script.Emit(OpCode.PACK); - script.EmitPush(2); // ParamCount - script.Emit(OpCode.PUSHA, BitConverter.GetBytes(200)); // -> Nop area - script.EmitSysCall(ApplicationEngine.System_Callback_Create); - script.EmitSysCall(ApplicationEngine.System_Callback_Invoke); - script.Emit(OpCode.RET); - - for (int x = 0; x < 250; x++) script.Emit(OpCode.NOP); - - script.Emit(OpCode.SUB); // Should return 5-1 - script.Emit(OpCode.RET); - - // Execute - - var engine = ApplicationEngine.Create(TriggerType.Application, null, null, 100_000_000); - engine.LoadScript(script.ToArray()); - Assert.AreEqual(engine.Execute(), VMState.HALT); - - // Check the results - - Assert.AreEqual(1, engine.ResultStack.Count); - var item = engine.ResultStack.Pop(); - Assert.AreEqual(4, item.GetInteger()); - } - - [TestMethod] - public void CreateSyscallCallbackTest() - { - using var script = new ScriptBuilder(); - - script.EmitPush(System.Array.Empty()); // Empty buffer - script.EmitPush(1); - script.Emit(OpCode.PACK); - script.EmitPush(ApplicationEngine.Neo_Crypto_SHA256.Hash); // Syscall - script.EmitSysCall(ApplicationEngine.System_Callback_CreateFromSyscall); - script.EmitSysCall(ApplicationEngine.System_Callback_Invoke); - - // Execute - - var engine = ApplicationEngine.Create(TriggerType.Application, null, null, 100_000_000); - engine.LoadScript(script.ToArray()); - Assert.AreEqual(engine.Execute(), VMState.HALT); - - // Check the results - - Assert.AreEqual(1, engine.ResultStack.Count); - var item = engine.ResultStack.Pop(); - Assert.AreEqual("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", item.GetSpan().ToHexString()); - } - } -} From 32513ed64a2ca648815db450c68a97b90f68bf32 Mon Sep 17 00:00:00 2001 From: Shine Li Date: Fri, 25 Dec 2020 14:15:10 +0800 Subject: [PATCH 05/65] upgrade package references (#2179) --- src/neo/neo.csproj | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 70f26f4593..40f9bd4524 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -21,13 +21,14 @@ - + + + + + - - - - + From 5052317189fa03c940a9a9ceff9e6b14899f0a30 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 27 Dec 2020 19:41:01 +0800 Subject: [PATCH 06/65] Fix native parameters (#2183) --- src/neo/SmartContract/Native/NativeContract.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 2b704e0510..391c01eb95 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -167,6 +167,8 @@ private static ContractParameterType ToParameterType(Type type) if (type == typeof(BigInteger)) return ContractParameterType.Integer; if (type == typeof(byte[])) return ContractParameterType.ByteArray; if (type == typeof(string)) return ContractParameterType.String; + if (type == typeof(UInt160)) return ContractParameterType.Hash160; + if (type == typeof(UInt256)) return ContractParameterType.Hash256; if (type == typeof(VM.Types.Boolean)) return ContractParameterType.Boolean; if (type == typeof(Integer)) return ContractParameterType.Integer; if (type == typeof(ByteString)) return ContractParameterType.ByteArray; From 91fb44cd0036c7ff57f5954555d2c1c870a29961 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 28 Dec 2020 18:49:58 +0800 Subject: [PATCH 07/65] Dynamic Call (#2167) --- .../Network/P2P/Payloads/OracleResponse.cs | 2 +- .../ApplicationEngine.Contract.cs | 35 +++---- .../ApplicationEngine.OpCodePrices.cs | 1 + src/neo/SmartContract/ApplicationEngine.cs | 97 ++++--------------- src/neo/SmartContract/Helper.cs | 19 +++- .../SmartContract/Native/NativeContract.cs | 10 +- src/neo/VM/Helper.cs | 37 +++---- src/neo/Wallets/AssetDescriptor.cs | 4 +- src/neo/Wallets/Wallet.cs | 13 +-- src/neo/neo.csproj | 2 +- .../Extensions/NativeContractExtensions.cs | 10 +- .../Nep17NativeContractExtensions.cs | 20 +--- .../Network/P2P/Payloads/UT_Transaction.cs | 14 +-- .../SmartContract/Native/UT_NativeContract.cs | 4 +- .../SmartContract/Native/UT_NeoToken.cs | 36 +++---- .../SmartContract/UT_Contract.cs | 4 +- .../neo.UnitTests/SmartContract/UT_Helper.cs | 4 +- .../SmartContract/UT_InteropService.NEO.cs | 6 +- .../SmartContract/UT_InteropService.cs | 47 +++------ .../SmartContract/UT_OpCodePrices.cs | 2 +- .../SmartContract/UT_Syscalls.cs | 8 +- tests/neo.UnitTests/VM/UT_Helper.cs | 45 ++++----- 22 files changed, 157 insertions(+), 263 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/OracleResponse.cs b/src/neo/Network/P2P/Payloads/OracleResponse.cs index c4043a3914..25f7797dbf 100644 --- a/src/neo/Network/P2P/Payloads/OracleResponse.cs +++ b/src/neo/Network/P2P/Payloads/OracleResponse.cs @@ -31,7 +31,7 @@ public class OracleResponse : TransactionAttribute static OracleResponse() { using ScriptBuilder sb = new ScriptBuilder(); - sb.EmitAppCall(NativeContract.Oracle.Hash, "finish"); + sb.EmitDynamicCall(NativeContract.Oracle.Hash, "finish", false); FixedScript = sb.ToArray(); } diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 7d43efe74e..5f274359cf 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -3,14 +3,14 @@ using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; using Neo.VM; +using Neo.VM.Types; using System; -using Array = Neo.VM.Types.Array; namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Contract_CallEx = Register("System.Contract.CallEx", nameof(CallContractEx), 1 << 15, CallFlags.AllowCall); + public static readonly InteropDescriptor System_Contract_Call = Register("System.Contract.Call", nameof(CallContract), 1 << 15, CallFlags.AllowCall); public static readonly InteropDescriptor System_Contract_CallNative = Register("System.Contract.CallNative", nameof(CallNativeContract), 0, CallFlags.None); public static readonly InteropDescriptor System_Contract_IsStandard = Register("System.Contract.IsStandard", nameof(IsStandardContract), 1 << 10, CallFlags.ReadStates); public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 1 << 10, CallFlags.None); @@ -22,15 +22,20 @@ partial class ApplicationEngine public static readonly InteropDescriptor System_Contract_NativeOnPersist = Register("System.Contract.NativeOnPersist", nameof(NativeOnPersist), 0, CallFlags.WriteStates); public static readonly InteropDescriptor System_Contract_NativePostPersist = Register("System.Contract.NativePostPersist", nameof(NativePostPersist), 0, CallFlags.WriteStates); - protected internal void CallContractEx(UInt160 contractHash, string method, Array args, CallFlags callFlags) + protected internal void CallContract(UInt160 contractHash, string method, CallFlags callFlags, bool hasReturnValue, ushort pcount) { if (method.StartsWith('_')) throw new ArgumentException($"Invalid Method Name: {method}"); if ((callFlags & ~CallFlags.All) != 0) throw new ArgumentOutOfRangeException(nameof(callFlags)); - CallContractInternal(contractHash, method, args, callFlags, ReturnTypeConvention.EnsureNotEmpty); + if (pcount > CurrentContext.EvaluationStack.Count) + throw new InvalidOperationException(); + StackItem[] args = new StackItem[pcount]; + for (int i = 0; i < pcount; i++) + args[i] = Pop(); + CallContractInternal(contractHash, method, callFlags, hasReturnValue, args); } - private void CallContractInternal(UInt160 contractHash, string method, Array args, CallFlags flags, ReturnTypeConvention convention) + private void CallContractInternal(UInt160 contractHash, string method, CallFlags flags, bool hasReturnValue, StackItem[] args) { ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, contractHash); if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}"); @@ -48,10 +53,10 @@ private void CallContractInternal(UInt160 contractHash, string method, Array arg throw new InvalidOperationException($"Cannot Call Method {method} Of Contract {contractHash} From Contract {CurrentScriptHash}"); } - CallContractInternal(contract, md, args, flags, convention); + CallContractInternal(contract, md, flags, hasReturnValue, args); } - private void CallContractInternal(ContractState contract, ContractMethodDescriptor method, Array args, CallFlags flags, ReturnTypeConvention convention) + private void CallContractInternal(ContractState contract, ContractMethodDescriptor method, CallFlags flags, bool hasReturnValue, StackItem[] args) { if (invocationCounter.TryGetValue(contract.Hash, out var counter)) { @@ -62,27 +67,19 @@ private void CallContractInternal(ContractState contract, ContractMethodDescript invocationCounter[contract.Hash] = 1; } - GetInvocationState(CurrentContext).Convention = convention; - ExecutionContextState state = CurrentContext.GetState(); UInt160 callingScriptHash = state.ScriptHash; CallFlags callingFlags = state.CallFlags; - if (args.Count != method.Parameters.Length) throw new InvalidOperationException($"Method {method.Name} Expects {method.Parameters.Length} Arguments But Receives {args.Count} Arguments"); - ExecutionContext context_new = LoadContract(contract, method.Name, flags & callingFlags, false); + if (args.Length != method.Parameters.Length) throw new InvalidOperationException($"Method {method.Name} Expects {method.Parameters.Length} Arguments But Receives {args.Length} Arguments"); + ExecutionContext context_new = LoadContract(contract, method.Name, flags & callingFlags, hasReturnValue, (ushort)args.Length); state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; + for (int i = args.Length - 1; i >= 0; i--) + context_new.EvaluationStack.Push(args[i]); if (NativeContract.IsNative(contract.Hash)) - { - context_new.EvaluationStack.Push(args); context_new.EvaluationStack.Push(method.Name); - } - else - { - for (int i = args.Count - 1; i >= 0; i--) - context_new.EvaluationStack.Push(args[i]); - } } protected internal void CallNativeContract(string name) diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index d7a3b5b604..76a8f75842 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -58,6 +58,7 @@ partial class ApplicationEngine [OpCode.CALL] = 1 << 9, [OpCode.CALL_L] = 1 << 9, [OpCode.CALLA] = 1 << 9, + [OpCode.CALLT] = 1 << 15, [OpCode.ABORT] = 0, [OpCode.ASSERT] = 1 << 0, [OpCode.THROW] = 1 << 9, diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 9c02bbab60..96536a6639 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -20,18 +20,6 @@ namespace Neo.SmartContract { public partial class ApplicationEngine : ExecutionEngine { - private enum ReturnTypeConvention : byte - { - None = 0, - EnsureIsEmpty = 1, - EnsureNotEmpty = 2 - } - - private class InvocationState - { - public ReturnTypeConvention Convention; - } - /// /// This constant can be used for testing scripts. /// @@ -46,7 +34,6 @@ private class InvocationState private List notifications; private List disposables; private readonly Dictionary invocationCounter = new Dictionary(); - private readonly Dictionary invocationStates = new Dictionary(); private readonly uint exec_fee_factor; internal readonly uint StoragePrice; @@ -88,7 +75,7 @@ protected override void OnFault(Exception e) internal void CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, string method, params StackItem[] args) { - CallContractInternal(hash, method, new VMArray(ReferenceCounter, args), CallFlags.All, ReturnTypeConvention.EnsureIsEmpty); + CallContractInternal(hash, method, CallFlags.All, false, args); ExecutionContextState state = CurrentContext.GetState(); state.CallingScriptHash = callingScriptHash; StepOut(); @@ -96,51 +83,19 @@ internal void CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, st internal T CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, string method, params StackItem[] args) { - CallFromNativeContract(callingScriptHash, hash, method, args); + CallContractInternal(hash, method, CallFlags.All, true, args); + ExecutionContextState state = CurrentContext.GetState(); + state.CallingScriptHash = callingScriptHash; + StepOut(); return (T)Convert(Pop(), new InteropParameterDescriptor(typeof(T))); } - protected override void ContextUnloaded(ExecutionContext context) - { - base.ContextUnloaded(context); - if (!(UncaughtException is null)) return; - if (invocationStates.Count == 0) return; - if (!invocationStates.Remove(CurrentContext, out InvocationState state)) return; - switch (state.Convention) - { - case ReturnTypeConvention.EnsureIsEmpty: - { - if (context.EvaluationStack.Count != 0) - throw new InvalidOperationException(); - break; - } - case ReturnTypeConvention.EnsureNotEmpty: - { - if (context.EvaluationStack.Count == 0) - Push(StackItem.Null); - else if (context.EvaluationStack.Count > 1) - throw new InvalidOperationException(); - break; - } - } - } - public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas = TestModeGas) { return applicationEngineProvider?.Create(trigger, container, snapshot, gas) ?? new ApplicationEngine(trigger, container, snapshot, gas); } - private InvocationState GetInvocationState(ExecutionContext context) - { - if (!invocationStates.TryGetValue(context, out InvocationState state)) - { - state = new InvocationState(); - invocationStates.Add(context, state); - } - return state; - } - protected override void LoadContext(ExecutionContext context) { // Set default execution context state @@ -152,42 +107,32 @@ protected override void LoadContext(ExecutionContext context) base.LoadContext(context); } - public ExecutionContext LoadContract(ContractState contract, string method, CallFlags callFlags, bool packParameters = false) + public ExecutionContext LoadContract(ContractState contract, string method, CallFlags callFlags, bool hasReturnValue, ushort pcount) { ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); if (md is null) return null; - ExecutionContext context = LoadScript(contract.Script, callFlags, contract.Hash, md.Offset); + ExecutionContext context = LoadScript(contract.Script, + pcount: pcount, + rvcount: hasReturnValue ? 1 : 0, + initialPosition: md.Offset, + callFlags: callFlags, + scriptHash: contract.Hash); - if (NativeContract.IsNative(contract.Hash)) - { - if (packParameters) - { - using ScriptBuilder sb = new ScriptBuilder(); - sb.Emit(OpCode.DEPTH, OpCode.PACK); - sb.EmitPush(md.Name); - LoadScript(sb.ToArray(), CallFlags.None); - } - } - else + // Call initialization + var init = contract.Manifest.Abi.GetMethod("_initialize"); + if (init != null) { - // Call initialization - - var init = contract.Manifest.Abi.GetMethod("_initialize"); - - if (init != null) - { - LoadContext(context.Clone(init.Offset)); - } + LoadContext(context.Clone(init.Offset)); } return context; } - public ExecutionContext LoadScript(Script script, CallFlags callFlags, UInt160 scriptHash = null, int initialPosition = 0) + public ExecutionContext LoadScript(Script script, ushort pcount = 0, int rvcount = -1, int initialPosition = 0, CallFlags callFlags = CallFlags.All, UInt160 scriptHash = null) { // Create and configure context - ExecutionContext context = CreateContext(script, initialPosition); + ExecutionContext context = CreateContext(script, pcount, rvcount, initialPosition); var state = context.GetState(); state.CallFlags = callFlags; state.ScriptHash = scriptHash ?? ((byte[])script).ToScriptHash(); @@ -294,13 +239,13 @@ protected override void PreExecuteInstruction() AddGas(exec_fee_factor * OpCodePrices[CurrentContext.CurrentInstruction.OpCode]); } - private void StepOut() + internal void StepOut() { int c = InvocationStack.Count; while (State != VMState.HALT && State != VMState.FAULT && InvocationStack.Count >= c) ExecuteNext(); if (State == VMState.FAULT) - throw new InvalidOperationException("Call from native contract failed.", FaultException); + throw new InvalidOperationException("StepOut failed.", FaultException); } private static Block CreateDummyBlock(StoreView snapshot) @@ -350,7 +295,7 @@ public static ApplicationEngine Run(byte[] script, StoreView snapshot = null, IV snapshot.PersistingBlock = persistingBlock ?? snapshot.PersistingBlock ?? CreateDummyBlock(snapshot); ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, gas); if (disposable != null) engine.Disposables.Add(disposable); - engine.LoadScript(script, offset); + engine.LoadScript(script, initialPosition: offset); engine.Execute(); return engine; } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 3ac1532a68..a84c9ec1c7 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -187,19 +187,30 @@ internal static bool VerifyWitness(this IVerifiable verifiable, StoreView snapsh { ContractState cs = NativeContract.ContractManagement.GetContract(snapshot, hash); if (cs is null) return false; - if (engine.LoadContract(cs, "verify", callFlags, true) is null) + if (engine.LoadContract(cs, "verify", callFlags, true, 0) is null) return false; } else { if (NativeContract.IsNative(hash)) return false; if (hash != witness.ScriptHash) return false; - engine.LoadScript(verification, callFlags, hash, 0); + engine.LoadScript(verification, callFlags: callFlags, scriptHash: hash, initialPosition: 0); + } + + engine.LoadScript(witness.InvocationScript, callFlags: CallFlags.None); + + if (NativeContract.IsNative(hash)) + { + try + { + engine.StepOut(); + engine.Push("verify"); + } + catch { } } - engine.LoadScript(witness.InvocationScript, CallFlags.None); if (engine.Execute() == VMState.FAULT) return false; - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Peek().GetBoolean()) return false; + if (!engine.ResultStack.Peek().GetBoolean()) return false; fee = engine.GasConsumed; } return true; diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 391c01eb95..ad4adeef5c 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -107,7 +107,6 @@ internal void Invoke(ApplicationEngine engine) throw new InvalidOperationException("It is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used."); ExecutionContext context = engine.CurrentContext; string operation = context.EvaluationStack.Pop().GetString(); - Array args = context.EvaluationStack.Pop(); ContractMethodMetadata method = methods[operation]; ExecutionContextState state = context.GetState(); if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) @@ -117,10 +116,7 @@ internal void Invoke(ApplicationEngine engine) if (method.NeedApplicationEngine) parameters.Add(engine); if (method.NeedSnapshot) parameters.Add(engine.Snapshot); for (int i = 0; i < method.Parameters.Length; i++) - { - StackItem item = i < args.Count ? args[i] : StackItem.Null; - parameters.Add(engine.Convert(item, method.Parameters[i])); - } + parameters.Add(engine.Convert(context.EvaluationStack.Pop(), method.Parameters[i])); object returnValue = method.Handler.Invoke(this, parameters.ToArray()); if (method.Handler.ReturnType != typeof(void)) context.EvaluationStack.Push(engine.Convert(returnValue)); @@ -143,11 +139,11 @@ internal virtual void PostPersist(ApplicationEngine engine) { } - public ApplicationEngine TestCall(string operation, params object[] args) + public ApplicationEngine TestCall(string operation, bool hasReturnValue, params object[] args) { using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitAppCall(Hash, operation, args); + sb.EmitDynamicCall(Hash, operation, hasReturnValue, args); return ApplicationEngine.Run(sb.ToArray()); } } diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 4c56216113..257de647e4 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -46,40 +46,40 @@ public static ScriptBuilder Emit(this ScriptBuilder sb, params OpCode[] ops) return sb; } - public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, string operation) + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, bool hasReturnValue) { - sb.EmitPush(CallFlags.All); sb.EmitPush(0); - sb.Emit(OpCode.NEWARRAY); + sb.EmitPush(hasReturnValue ? 1 : 0); + sb.EmitPush(CallFlags.All); sb.EmitPush(operation); sb.EmitPush(scriptHash); - sb.EmitSysCall(ApplicationEngine.System_Contract_CallEx); + sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; } - public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params ContractParameter[] args) + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, bool hasReturnValue, params ContractParameter[] args) { - sb.EmitPush(CallFlags.All); for (int i = args.Length - 1; i >= 0; i--) sb.EmitPush(args[i]); sb.EmitPush(args.Length); - sb.Emit(OpCode.PACK); + sb.EmitPush(hasReturnValue ? 1 : 0); + sb.EmitPush(CallFlags.All); sb.EmitPush(operation); sb.EmitPush(scriptHash); - sb.EmitSysCall(ApplicationEngine.System_Contract_CallEx); + sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; } - public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params object[] args) + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, bool hasReturnValue, params object[] args) { - sb.EmitPush(CallFlags.All); for (int i = args.Length - 1; i >= 0; i--) sb.EmitPush(args[i]); sb.EmitPush(args.Length); - sb.Emit(OpCode.PACK); + sb.EmitPush(hasReturnValue ? 1 : 0); + sb.EmitPush(CallFlags.All); sb.EmitPush(operation); sb.EmitPush(scriptHash); - sb.EmitSysCall(ApplicationEngine.System_Contract_CallEx); + sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; } @@ -204,16 +204,11 @@ public static ScriptBuilder EmitSysCall(this ScriptBuilder sb, uint method, para /// contract operation /// operation arguments /// - public static byte[] MakeScript(this UInt160 scriptHash, string operation, params object[] args) + public static byte[] MakeScript(this UInt160 scriptHash, string operation, bool hasReturnValue, params object[] args) { - using (ScriptBuilder sb = new ScriptBuilder()) - { - if (args.Length > 0) - sb.EmitAppCall(scriptHash, operation, args); - else - sb.EmitAppCall(scriptHash, operation); - return sb.ToArray(); - } + using ScriptBuilder sb = new ScriptBuilder(); + sb.EmitDynamicCall(scriptHash, operation, hasReturnValue, args); + return sb.ToArray(); } public static JObject ToJson(this StackItem item) diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index cd9fcb8859..e13fb02589 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -23,8 +23,8 @@ public AssetDescriptor(UInt160 asset_id) byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitAppCall(asset_id, "decimals"); - sb.EmitAppCall(asset_id, "symbol"); + sb.EmitDynamicCall(asset_id, "decimals", true); + sb.EmitDynamicCall(asset_id, "symbol", true); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, gas: 0_10000000); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index a5f6c9b13f..44cb5990f3 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -138,10 +138,10 @@ public BigDecimal GetBalance(UInt160 asset_id, params UInt160[] accounts) sb.EmitPush(0); foreach (UInt160 account in accounts) { - sb.EmitAppCall(asset_id, "balanceOf", account); + sb.EmitDynamicCall(asset_id, "balanceOf", true, account); sb.Emit(OpCode.ADD); } - sb.EmitAppCall(asset_id, "decimals"); + sb.EmitDynamicCall(asset_id, "decimals", true); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, gas: 20000000L * accounts.Length); @@ -265,7 +265,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null foreach (UInt160 account in accounts) using (ScriptBuilder sb2 = new ScriptBuilder()) { - sb2.EmitAppCall(assetId, "balanceOf", account); + sb2.EmitDynamicCall(assetId, "balanceOf", true, account); using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot)) { if (engine.State.HasFlag(VMState.FAULT)) @@ -296,7 +296,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null Scopes = WitnessScope.CalledByEntry }); } - sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value, output.Data); + sb.EmitDynamicCall(output.AssetId, "transfer", true, account, output.ScriptHash, value, output.Data); sb.Emit(OpCode.ASSERT); } } @@ -397,10 +397,11 @@ public long CalculateNetworkFee(StoreView snapshot, Transaction tx) // Check verify cost using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.Clone()); - if (engine.LoadContract(contract, "verify", CallFlags.None, true) is null) + if (engine.LoadContract(contract, "verify", CallFlags.None, true, 0) is null) throw new ArgumentException($"The smart contract {contract.Hash} haven't got verify method"); + if (NativeContract.IsNative(hash)) engine.Push("verify"); if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false."); + if (!engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false."); networkFee += engine.GasConsumed; } diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 40f9bd4524..f0ff860c79 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -29,7 +29,7 @@ - + diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index 38c5ef651b..99c6497fb7 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -13,7 +13,7 @@ public static class NativeContractExtensions public static ContractState DeployContract(this StoreView snapshot, UInt160 sender, byte[] nefFile, byte[] manifest, long gas = 200_00000000) { var script = new ScriptBuilder(); - script.EmitAppCall(NativeContract.ContractManagement.Hash, "deploy", nefFile, manifest); + script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", true, nefFile, manifest); var engine = ApplicationEngine.Create(TriggerType.Application, sender != null ? new Transaction() { Signers = new Signer[] { new Signer() { Account = sender } } } : null, snapshot, gas); @@ -34,7 +34,7 @@ public static ContractState DeployContract(this StoreView snapshot, UInt160 send public static void UpdateContract(this StoreView snapshot, UInt160 callingScriptHash, byte[] nefFile, byte[] manifest) { var script = new ScriptBuilder(); - script.EmitAppCall(NativeContract.ContractManagement.Hash, "update", nefFile, manifest); + script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "update", false, nefFile, manifest); var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(script.ToArray()); @@ -57,7 +57,7 @@ public static void UpdateContract(this StoreView snapshot, UInt160 callingScript public static void DestroyContract(this StoreView snapshot, UInt160 callingScriptHash) { var script = new ScriptBuilder(); - script.EmitAppCall(NativeContract.ContractManagement.Hash, "destroy"); + script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "destroy", false); var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(script.ToArray()); @@ -100,13 +100,13 @@ public static StackItem Call(this NativeContract contract, StoreView snapshot, I var contractState = NativeContract.ContractManagement.GetContract(snapshot, contract.Hash); if (contractState == null) throw new InvalidOperationException(); - engine.LoadContract(contractState, method, CallFlags.All, true); - var script = new ScriptBuilder(); for (var i = args.Length - 1; i >= 0; i--) script.EmitPush(args[i]); + script.EmitPush(method); + engine.LoadContract(contractState, method, CallFlags.All, contract.Manifest.Abi.GetMethod(method).ReturnType != ContractParameterType.Void, (ushort)args.Length); engine.LoadScript(script.ToArray()); if (engine.Execute() != VMState.HALT) diff --git a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs index 6b87c22863..942993b5e6 100644 --- a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs @@ -40,15 +40,13 @@ public static bool Transfer(this NativeContract contract, StoreView snapshot, by var engine = ApplicationEngine.Create(TriggerType.Application, new ManualWitness(signFrom ? new UInt160(from) : null), snapshot); - engine.LoadScript(contract.Script, CallFlags.All, contract.Hash); + engine.LoadScript(contract.Script, pcount: 4, callFlags: CallFlags.All, scriptHash: contract.Hash); var script = new ScriptBuilder(); script.Emit(OpCode.PUSHNULL); script.EmitPush(amount); script.EmitPush(to); script.EmitPush(from); - script.EmitPush(4); - script.Emit(OpCode.PACK); script.EmitPush("transfer"); engine.LoadScript(script.ToArray()); @@ -67,11 +65,9 @@ public static BigInteger TotalSupply(this NativeContract contract, StoreView sna { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, CallFlags.All, contract.Hash); + engine.LoadScript(contract.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: contract.Hash); var script = new ScriptBuilder(); - script.EmitPush(0); - script.Emit(OpCode.PACK); script.EmitPush("totalSupply"); engine.LoadScript(script.ToArray()); @@ -87,12 +83,10 @@ public static BigInteger BalanceOf(this NativeContract contract, StoreView snaps { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, CallFlags.All, contract.Hash); + engine.LoadScript(contract.Script, pcount: 1, callFlags: CallFlags.All, scriptHash: contract.Hash); var script = new ScriptBuilder(); script.EmitPush(account); - script.EmitPush(1); - script.Emit(OpCode.PACK); script.EmitPush("balanceOf"); engine.LoadScript(script.ToArray()); @@ -108,11 +102,9 @@ public static BigInteger Decimals(this NativeContract contract, StoreView snapsh { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, CallFlags.All, contract.Hash); + engine.LoadScript(contract.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: contract.Hash); var script = new ScriptBuilder(); - script.EmitPush(0); - script.Emit(OpCode.PACK); script.EmitPush("decimals"); engine.LoadScript(script.ToArray()); @@ -128,11 +120,9 @@ public static string Symbol(this NativeContract contract, StoreView snapshot) { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, CallFlags.All, contract.Hash); + engine.LoadScript(contract.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: contract.Hash); var script = new ScriptBuilder(); - script.EmitPush(0); - script.Emit(OpCode.PACK); script.EmitPush("symbol"); engine.LoadScript(script.ToArray()); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index ff47f3b6ee..14c5afa0e9 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -319,7 +319,7 @@ public void FeeIsSignatureContract_TestScope_Global() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -405,7 +405,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -492,7 +492,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() { // self-transfer of 1e-8 GAS System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -580,7 +580,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() { // self-transfer of 1e-8 GAS System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -632,7 +632,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -722,7 +722,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -985,7 +985,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 90b54ff208..f7ccdd206d 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -99,7 +99,7 @@ public void TestInvoke() { var snapshot = Blockchain.Singleton.GetSnapshot(); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, 0); - engine.LoadScript(testNativeContract.Script, CallFlags.All, testNativeContract.Hash); + engine.LoadScript(testNativeContract.Script, callFlags: CallFlags.All, scriptHash: testNativeContract.Hash); ByteString method1 = new ByteString(System.Text.Encoding.Default.GetBytes("wrongMethod")); VMArray args1 = new VMArray(); @@ -129,7 +129,7 @@ public void TestTrigger() [TestMethod] public void TestTestCall() { - ApplicationEngine engine = testNativeContract.TestCall("System.Blockchain.GetHeight", 0); + ApplicationEngine engine = testNativeContract.TestCall("System.Blockchain.GetHeight", false, 0); engine.ResultStack.Should().BeEmpty(); } } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 5ab79afe5f..02899a5a71 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -509,7 +509,7 @@ public void TestCalculateBonus() [TestMethod] public void TestGetNextBlockValidators1() { - using (ApplicationEngine engine = NativeContract.NEO.TestCall("getNextBlockValidators")) + using (ApplicationEngine engine = NativeContract.NEO.TestCall("getNextBlockValidators", true)) { var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); @@ -542,7 +542,7 @@ public void TestGetNextBlockValidators2() [TestMethod] public void TestGetCandidates1() { - using ApplicationEngine engine = NativeContract.NEO.TestCall("getCandidates"); + using ApplicationEngine engine = NativeContract.NEO.TestCall("getCandidates", true); var array = engine.ResultStack.Pop(); array.Count.Should().Be(0); } @@ -603,7 +603,7 @@ public void TestCheckCandidate() [TestMethod] public void TestGetCommittee() { - using (ApplicationEngine engine = NativeContract.NEO.TestCall("getCommittee")) + using (ApplicationEngine engine = NativeContract.NEO.TestCall("getCommittee", true)) { var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); @@ -835,7 +835,7 @@ public void TestVote() })); } - sb.EmitAppCall(NativeContract.NEO.Hash, "transfer", from, UInt160.Zero, amount, null); + sb.EmitDynamicCall(NativeContract.NEO.Hash, "transfer", true, from, UInt160.Zero, amount, null); engine.LoadScript(sb.ToArray()); engine.Execute(); var result = engine.ResultStack.Peek(); @@ -867,11 +867,9 @@ internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView sn { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(NativeContract.NEO.Script, CallFlags.All, NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); var script = new ScriptBuilder(); - script.EmitPush(0); - script.Emit(OpCode.PACK); script.EmitPush("getGasPerBlock"); engine.LoadScript(script.ToArray()); @@ -891,12 +889,10 @@ internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreV UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot); - engine.LoadScript(NativeContract.NEO.Script, CallFlags.All, NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 1, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush(gasPerBlock); - script.EmitPush(1); - script.Emit(OpCode.PACK); script.EmitPush("setGasPerBlock"); engine.LoadScript(script.ToArray()); @@ -916,7 +912,7 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot); - engine.LoadScript(NativeContract.NEO.Script, CallFlags.All, NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 2, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); var script = new ScriptBuilder(); @@ -925,8 +921,6 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] else script.EmitPush(pubkey); script.EmitPush(account); - script.EmitPush(2); - script.Emit(OpCode.PACK); script.EmitPush("vote"); engine.LoadScript(script.ToArray()); @@ -946,12 +940,10 @@ internal static (bool State, bool Result) Check_RegisterValidator(StoreView snap var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot); - engine.LoadScript(NativeContract.NEO.Script, CallFlags.All, NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 1, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush(pubkey); - script.EmitPush(1); - script.Emit(OpCode.PACK); script.EmitPush("registerCandidate"); engine.LoadScript(script.ToArray()); @@ -970,11 +962,9 @@ internal static ECPoint[] Check_GetCommittee(StoreView snapshot) { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(NativeContract.NEO.Script, CallFlags.All, NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); var script = new ScriptBuilder(); - script.EmitPush(0); - script.Emit(OpCode.PACK); script.EmitPush("getCommittee"); engine.LoadScript(script.ToArray()); @@ -990,13 +980,11 @@ internal static (BigInteger Value, bool State) Check_UnclaimedGas(StoreView snap { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(NativeContract.NEO.Script, CallFlags.All, NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 2, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush(snapshot.PersistingBlock.Index); script.EmitPush(address); - script.EmitPush(2); - script.Emit(OpCode.PACK); script.EmitPush("unclaimedGas"); engine.LoadScript(script.ToArray()); @@ -1052,12 +1040,10 @@ internal static (bool State, bool Result) Check_UnregisterCandidate(StoreView sn var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot); - engine.LoadScript(NativeContract.NEO.Script, CallFlags.All, NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 1, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush(pubkey); - script.EmitPush(1); - script.Emit(OpCode.PACK); script.EmitPush("unregisterCandidate"); engine.LoadScript(script.ToArray()); diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs index e2843ac0de..57cea86681 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Contract.cs @@ -180,7 +180,7 @@ public void TestSignatureRedeemScriptFee() using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null)) { - engine.LoadScript(invocation.Concat(verification).ToArray(), CallFlags.None); + engine.LoadScript(invocation.Concat(verification).ToArray(), callFlags: CallFlags.None); engine.Execute(); engine.GasConsumed.Should().Be(fee); } @@ -208,7 +208,7 @@ public void TestCreateMultiSigRedeemScriptFee() using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null)) { - engine.LoadScript(invocation.Concat(verification).ToArray(), CallFlags.None); + engine.LoadScript(invocation.Concat(verification).ToArray(), callFlags: CallFlags.None); engine.Execute(); engine.GasConsumed.Should().Be(fee); } diff --git a/tests/neo.UnitTests/SmartContract/UT_Helper.cs b/tests/neo.UnitTests/SmartContract/UT_Helper.cs index dc666fab20..3e5511f5a8 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Helper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Helper.cs @@ -17,8 +17,8 @@ public void TestGetContractHash() }; nef.CheckSum = NefFile.ComputeChecksum(nef); - Assert.AreEqual("0xb4b7417195feca1cdb5a99504ab641d8c220ae99", Neo.SmartContract.Helper.GetContractHash(UInt160.Zero, nef.Script).ToString()); - Assert.AreEqual("0xe56e4ee87f89a70e9138432c387ad49f2ee5b55f", Neo.SmartContract.Helper.GetContractHash(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), nef.Script).ToString()); + Assert.AreEqual("0xb8e95ff7b11c427c29355e3398722d97bd2ca069", Neo.SmartContract.Helper.GetContractHash(UInt160.Zero, nef.Script).ToString()); + Assert.AreEqual("0x435c467b8e15cb9b1474ad7ee817ffdcfededef9", Neo.SmartContract.Helper.GetContractHash(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), nef.Script).ToString()); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 09ab10c78b..0a28d223a9 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -154,15 +154,15 @@ public void TestContract_Create() script_exceedMaxLength.CheckSum = NefFile.ComputeChecksum(nef); Assert.ThrowsException(() => snapshot.DeployContract(UInt160.Zero, script_exceedMaxLength.ToArray(), manifest.ToJson().ToByteArray(true))); - var script_zeroLength = new byte[] { }; + var script_zeroLength = System.Array.Empty(); Assert.ThrowsException(() => snapshot.DeployContract(UInt160.Zero, script_zeroLength, manifest.ToJson().ToByteArray(true))); - var manifest_zeroLength = new byte[] { }; + var manifest_zeroLength = System.Array.Empty(); Assert.ThrowsException(() => snapshot.DeployContract(UInt160.Zero, nefFile, manifest_zeroLength)); manifest = TestUtils.CreateDefaultManifest(); var ret = snapshot.DeployContract(UInt160.Zero, nefFile, manifest.ToJson().ToByteArray(false)); - ret.Hash.ToString().Should().Be("0x5756874a149b9de89c7b5d34f9c37db3762f88a2"); + ret.Hash.ToString().Should().Be("0x6410935f6b153eeb85f5d95d926c075b1ef51620"); Assert.ThrowsException(() => snapshot.DeployContract(UInt160.Zero, nefFile, manifest.ToJson().ToByteArray(false))); var state = TestUtils.GetContract(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 8b1b1a8687..ba67c15287 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -89,7 +89,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitAppCall(scriptHash2, "test", "testEvent2", 1); + script.EmitDynamicCall(scriptHash2, "test", true, "testEvent2", 1); // Drop return @@ -141,7 +141,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitAppCall(scriptHash2, "test", "testEvent2", 1); + script.EmitDynamicCall(scriptHash2, "test", true, "testEvent2", 1); // Drop return @@ -224,7 +224,7 @@ public void TestExecutionEngine_GetCallingScriptHash() engine.Snapshot.AddContract(contract.Hash, contract); using ScriptBuilder scriptB = new ScriptBuilder(); - scriptB.EmitAppCall(contract.Hash, "test", 0, 1); + scriptB.EmitDynamicCall(contract.Hash, "test", true, 0, 1); engine.LoadScript(scriptB.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -598,46 +598,21 @@ public void TestContract_Call() var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); - engine.CallContractEx(state.Hash, method, args, CallFlags.All); + engine.Push(args[1]); engine.Push(args[0]); + engine.CallContract(state.Hash, method, CallFlags.All, false, (ushort)args.Count); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); state.Manifest.Permissions[0].Methods = WildcardContainer.Create("a"); - Assert.ThrowsException(() => engine.CallContractEx(state.Hash, method, args, CallFlags.All)); + engine.Push(args[1]); engine.Push(args[0]); + Assert.ThrowsException(() => engine.CallContract(state.Hash, method, CallFlags.All, false, (ushort)args.Count)); state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); - engine.CallContractEx(state.Hash, method, args, CallFlags.All); + engine.Push(args[1]); engine.Push(args[0]); + engine.CallContract(state.Hash, method, CallFlags.All, false, (ushort)args.Count); - Assert.ThrowsException(() => engine.CallContractEx(UInt160.Zero, method, args, CallFlags.All)); - } - - [TestMethod] - public void TestContract_CallEx() - { - var snapshot = Blockchain.Singleton.GetSnapshot(); - - string method = "method"; - var args = new VM.Types.Array { 0, 1 }; - var state = TestUtils.GetContract(method, args.Count); - snapshot.AddContract(state.Hash, state); - - foreach (var flags in new CallFlags[] { CallFlags.None, CallFlags.AllowCall, CallFlags.WriteStates, CallFlags.All }) - { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(new byte[] { 0x01 }); - - engine.CallContractEx(state.Hash, method, args, CallFlags.All); - engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); - engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); - - // Contract doesn't exists - Assert.ThrowsException(() => engine.CallContractEx(UInt160.Zero, method, args, CallFlags.All)); - - // Call with rights - engine.CallContractEx(state.Hash, method, args, flags); - engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); - engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); - } + engine.Push(args[1]); engine.Push(args[0]); + Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, method, CallFlags.All, false, (ushort)args.Count)); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_OpCodePrices.cs b/tests/neo.UnitTests/SmartContract/UT_OpCodePrices.cs index cb04990e31..fa82f194d2 100644 --- a/tests/neo.UnitTests/SmartContract/UT_OpCodePrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_OpCodePrices.cs @@ -12,7 +12,7 @@ public class UT_OpCodePrices public void AllOpcodePriceAreSet() { foreach (OpCode opcode in Enum.GetValues(typeof(OpCode))) - Assert.IsTrue(ApplicationEngine.OpCodePrices.ContainsKey(opcode)); + Assert.IsTrue(ApplicationEngine.OpCodePrices.ContainsKey(opcode), opcode.ToString()); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 844d798761..3a06e062a0 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -349,10 +349,10 @@ public void System_Runtime_GetInvocationCounter() using (var script = new ScriptBuilder()) { - script.EmitAppCall(contractA.Hash, "dummyMain", 0, 1); - script.EmitAppCall(contractB.Hash, "dummyMain", 0, 1); - script.EmitAppCall(contractB.Hash, "dummyMain", 0, 1); - script.EmitAppCall(contractC.Hash, "dummyMain", 0, 1); + script.EmitDynamicCall(contractA.Hash, "dummyMain", true, 0, 1); + script.EmitDynamicCall(contractB.Hash, "dummyMain", true, 0, 1); + script.EmitDynamicCall(contractB.Hash, "dummyMain", true, 0, 1); + script.EmitDynamicCall(contractC.Hash, "dummyMain", true, 0, 1); // Execute diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 252cb78566..cd84fdc773 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -50,23 +50,22 @@ public void TestToJson() [TestMethod] public void TestEmitAppCall1() { - //format:(byte)0x10+(byte)OpCode.NEWARRAY+(string)operation+(Uint160)scriptHash+(uint)InteropService.System_Contract_Call ScriptBuilder sb = new ScriptBuilder(); - sb.EmitAppCall(UInt160.Zero, "AAAAA"); + sb.EmitDynamicCall(UInt160.Zero, "AAAAA", false); byte[] tempArray = new byte[37]; - tempArray[0] = (byte)OpCode.PUSH15; + tempArray[0] = (byte)OpCode.PUSH0; tempArray[1] = (byte)OpCode.PUSH0; - tempArray[2] = (byte)OpCode.NEWARRAY; + tempArray[2] = (byte)OpCode.PUSH15;//(byte)CallFlags.All; tempArray[3] = (byte)OpCode.PUSHDATA1; tempArray[4] = 5;//operation.Length Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 5, 5);//operation.data tempArray[10] = (byte)OpCode.PUSHDATA1; tempArray[11] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 12, 20);//operation.data - uint api = ApplicationEngine.System_Contract_CallEx; + uint api = ApplicationEngine.System_Contract_Call; tempArray[32] = (byte)OpCode.SYSCALL; Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data - CollectionAssert.AreEqual(tempArray, sb.ToArray()); + Assert.AreEqual(tempArray.ToHexString(), sb.ToArray().ToHexString()); } [TestMethod] @@ -114,55 +113,53 @@ public void TestEmitMap() [TestMethod] public void TestEmitAppCall2() { - //format:(ContractParameter[])ContractParameter+(byte)OpCode.PACK+(string)operation+(Uint160)scriptHash+(uint)InteropService.System_Contract_Call ScriptBuilder sb = new ScriptBuilder(); - sb.EmitAppCall(UInt160.Zero, "AAAAA", new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) }); + sb.EmitDynamicCall(UInt160.Zero, "AAAAA", false, new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) }); byte[] tempArray = new byte[38]; - tempArray[0] = (byte)OpCode.PUSH15; - tempArray[1] = (byte)OpCode.PUSH0; - tempArray[2] = (byte)OpCode.PUSH1; - tempArray[3] = (byte)OpCode.PACK; + tempArray[0] = (byte)OpCode.PUSH0; + tempArray[1] = (byte)OpCode.PUSH1; + tempArray[2] = (byte)OpCode.PUSH0; + tempArray[3] = (byte)OpCode.PUSH15;//(byte)CallFlags.All; tempArray[4] = (byte)OpCode.PUSHDATA1; tempArray[5] = 0x05;//operation.Length Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 6, 5);//operation.data tempArray[11] = (byte)OpCode.PUSHDATA1; tempArray[12] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 13, 20);//operation.data - uint api = ApplicationEngine.System_Contract_CallEx; + uint api = ApplicationEngine.System_Contract_Call; tempArray[33] = (byte)OpCode.SYSCALL; Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 34, 4);//api.data - CollectionAssert.AreEqual(tempArray, sb.ToArray()); + Assert.AreEqual(tempArray.ToHexString(), sb.ToArray().ToHexString()); } [TestMethod] public void TestEmitAppCall3() { - //format:(object[])args+(byte)OpCode.PACK+(string)operation+(Uint160)scriptHash+(uint)InteropService.System_Contract_Call ScriptBuilder sb = new ScriptBuilder(); - sb.EmitAppCall(UInt160.Zero, "AAAAA", true); + sb.EmitDynamicCall(UInt160.Zero, "AAAAA", false, true); byte[] tempArray = new byte[38]; - tempArray[0] = (byte)OpCode.PUSH15; - tempArray[1] = (byte)OpCode.PUSH1;//arg - tempArray[2] = (byte)OpCode.PUSH1;//args.Length - tempArray[3] = (byte)OpCode.PACK; + tempArray[0] = (byte)OpCode.PUSH1; + tempArray[1] = (byte)OpCode.PUSH1;//arg.Length + tempArray[2] = (byte)OpCode.PUSH0;//return + tempArray[3] = (byte)OpCode.PUSH15;//(byte)CallFlags.All; tempArray[4] = (byte)OpCode.PUSHDATA1; tempArray[5] = 0x05;//operation.Length Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 6, 5);//operation.data tempArray[11] = (byte)OpCode.PUSHDATA1; tempArray[12] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 13, 20);//operation.data - uint api = ApplicationEngine.System_Contract_CallEx; + uint api = ApplicationEngine.System_Contract_Call; tempArray[33] = (byte)OpCode.SYSCALL; Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 34, 4);//api.data - CollectionAssert.AreEqual(tempArray, sb.ToArray()); + Assert.AreEqual(tempArray.ToHexString(), sb.ToArray().ToHexString()); } [TestMethod] public void TestMakeScript() { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); + byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", true, UInt160.Zero); - Assert.AreEqual("1f0c14000000000000000000000000000000000000000011c00c0962616c616e63654f660c14fbedfe2ed2226592b648c4da97b9c9cd5dc1a6a641eef40cdb", + Assert.AreEqual("0c14000000000000000000000000000000000000000011111f0c0962616c616e63654f660c141717ddafdd757eec365865b963473beb617f9a1441627d5b52", testScript.ToHexString()); } From 6626b2bed35cf15ba2ed03bc9938ca09982d67e1 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 28 Dec 2020 18:52:44 +0800 Subject: [PATCH 08/65] Add MethodToken (#2185) --- src/neo/SmartContract/MethodToken.cs | 42 +++++++++++++++ src/neo/SmartContract/NefFile.cs | 45 ++++++++++++---- .../neo.UnitTests/SmartContract/UT_Helper.cs | 2 + .../SmartContract/UT_InteropService.NEO.cs | 15 ++++-- .../SmartContract/UT_MethodToken.cs | 51 +++++++++++++++++++ .../neo.UnitTests/SmartContract/UT_NefFile.cs | 5 +- 6 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 src/neo/SmartContract/MethodToken.cs create mode 100644 tests/neo.UnitTests/SmartContract/UT_MethodToken.cs diff --git a/src/neo/SmartContract/MethodToken.cs b/src/neo/SmartContract/MethodToken.cs new file mode 100644 index 0000000000..10396757a7 --- /dev/null +++ b/src/neo/SmartContract/MethodToken.cs @@ -0,0 +1,42 @@ +using Neo.IO; +using System; +using System.IO; + +namespace Neo.SmartContract +{ + public class MethodToken : ISerializable + { + public UInt160 Hash; + public string Method; + public ushort ParametersCount; + public ushort RVCount; + public CallFlags CallFlags; + + public int Size => + UInt160.Length + // Hash + Method.GetVarSize() + // Method + sizeof(ushort) + // ParametersCount + sizeof(ushort) + // RVCount + sizeof(CallFlags); // CallFlags + + void ISerializable.Deserialize(BinaryReader reader) + { + Hash = reader.ReadSerializable(); + Method = reader.ReadVarString(32); + ParametersCount = reader.ReadUInt16(); + RVCount = reader.ReadUInt16(); + CallFlags = (CallFlags)reader.ReadByte(); + if ((CallFlags & ~CallFlags.All) != 0) + throw new FormatException(); + } + + void ISerializable.Serialize(BinaryWriter writer) + { + writer.Write(Hash); + writer.WriteVarString(Method); + writer.Write(ParametersCount); + writer.Write(RVCount); + writer.Write((byte)CallFlags); + } + } +} diff --git a/src/neo/SmartContract/NefFile.cs b/src/neo/SmartContract/NefFile.cs index 76d3d94dc2..07c9801cbd 100644 --- a/src/neo/SmartContract/NefFile.cs +++ b/src/neo/SmartContract/NefFile.cs @@ -6,17 +6,22 @@ namespace Neo.SmartContract { /// - /// +------------+-----------+------------------------------------------------------------+ - /// | Field | Length | Comment | - /// +------------+-----------+------------------------------------------------------------+ - /// | Magic | 4 bytes | Magic header | - /// | Compiler | 32 bytes | Compiler used | - /// | Version | 32 bytes | Compiler version | - /// +------------+-----------+------------------------------------------------------------+ - /// | Script | Var bytes | Var bytes for the payload | - /// +------------+-----------+------------------------------------------------------------+ - /// | Checksum | 4 bytes | First four bytes of double SHA256 hash | - /// +------------+-----------+------------------------------------------------------------+ + /// ┌───────────────────────────────────────────────────────────────────────┐ + /// │ NEO Executable Format 3 (NEF3) │ + /// ├──────────┬───────────────┬────────────────────────────────────────────┤ + /// │ Field │ Type │ Comment │ + /// ├──────────┼───────────────┼────────────────────────────────────────────┤ + /// │ Magic │ uint32 │ Magic header │ + /// │ Compiler │ byte[32] │ Compiler used │ + /// │ Version │ byte[32] │ Compiler version │ + /// ├──────────┼───────────────┼────────────────────────────────────────────┤ + /// │ Reserve │ byte[2] │ Reserved for future extensions. Must be 0. │ + /// │ Tokens │ MethodToken[] │ Method tokens. │ + /// │ Reserve │ byte[2] │ Reserved for future extensions. Must be 0. │ + /// │ Script │ byte[] │ Var bytes for the payload │ + /// ├──────────┼───────────────┼────────────────────────────────────────────┤ + /// │ Checksum │ uint32 │ First four bytes of double SHA256 hash │ + /// └──────────┴───────────────┴────────────────────────────────────────────┘ /// public class NefFile : ISerializable { @@ -35,6 +40,11 @@ public class NefFile : ISerializable /// public string Version { get; set; } + /// + /// Method tokens + /// + public MethodToken[] Tokens { get; set; } + /// /// Script /// @@ -53,12 +63,18 @@ public class NefFile : ISerializable public int Size => HeaderSize + // Header + 2 + // Reserve + Tokens.GetVarSize() + // Tokens + 2 + // Reserve Script.GetVarSize() + // Script sizeof(uint); // Checksum public void Serialize(BinaryWriter writer) { SerializeHeader(writer); + writer.Write((short)0); + writer.Write(Tokens); + writer.Write((short)0); writer.WriteVarBytes(Script ?? Array.Empty()); writer.Write(CheckSum); } @@ -78,6 +94,13 @@ public void Deserialize(BinaryReader reader) Compiler = reader.ReadFixedString(32); Version = reader.ReadFixedString(32); + + if (reader.ReadUInt16() != 0) throw new FormatException("Reserved bytes must be 0"); + + Tokens = reader.ReadSerializableArray(128); + + if (reader.ReadUInt16() != 0) throw new FormatException("Reserved bytes must be 0"); + Script = reader.ReadVarBytes(MaxScriptLength); if (Script.Length == 0) throw new ArgumentException($"Script can't be empty"); diff --git a/tests/neo.UnitTests/SmartContract/UT_Helper.cs b/tests/neo.UnitTests/SmartContract/UT_Helper.cs index 3e5511f5a8..881bc7845f 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Helper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Helper.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; +using System; namespace Neo.UnitTests.SmartContract { @@ -13,6 +14,7 @@ public void TestGetContractHash() { Compiler = "test", Version = new System.Version().ToString(), + Tokens = Array.Empty(), Script = new byte[] { 1, 2, 3 } }; nef.CheckSum = NefFile.ComputeChecksum(nef); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 0a28d223a9..73146fefbd 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -136,7 +136,8 @@ public void TestContract_Create() { Script = new byte[byte.MaxValue], Compiler = "", - Version = new Version(1, 2, 3, 4).ToString() + Version = new Version(1, 2, 3, 4).ToString(), + Tokens = System.Array.Empty() }; nef.CheckSum = NefFile.ComputeChecksum(nef); var nefFile = nef.ToArray(); @@ -149,7 +150,8 @@ public void TestContract_Create() { Script = new byte[NefFile.MaxScriptLength - 1], Compiler = "", - Version = new Version(1, 2, 3, 4).ToString() + Version = new Version(1, 2, 3, 4).ToString(), + Tokens = System.Array.Empty() }; script_exceedMaxLength.CheckSum = NefFile.ComputeChecksum(nef); Assert.ThrowsException(() => snapshot.DeployContract(UInt160.Zero, script_exceedMaxLength.ToArray(), manifest.ToJson().ToByteArray(true))); @@ -181,7 +183,8 @@ public void TestContract_Update() { Script = new byte[] { 0x01 }, Compiler = "", - Version = new Version(1, 2, 3, 4).ToString() + Version = new Version(1, 2, 3, 4).ToString(), + Tokens = System.Array.Empty() }; nef.CheckSum = NefFile.ComputeChecksum(nef); Assert.ThrowsException(() => snapshot.UpdateContract(null, nef.ToArray(), new byte[0])); @@ -232,7 +235,8 @@ public void TestContract_Update_Invalid() { Script = new byte[] { 0x01 }, Version = new Version(1, 2, 3, 4).ToString(), - Compiler = "" + Compiler = "", + Tokens = System.Array.Empty() }; nefFile.CheckSum = NefFile.ComputeChecksum(nefFile); @@ -247,7 +251,8 @@ public void TestContract_Update_Invalid() { Script = new byte[0], Version = new Version(1, 2, 3, 4).ToString(), - Compiler = "" + Compiler = "", + Tokens = System.Array.Empty() }; nefFile.CheckSum = NefFile.ComputeChecksum(nefFile); diff --git a/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs b/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs new file mode 100644 index 0000000000..e200da4f62 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs @@ -0,0 +1,51 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.SmartContract; +using System; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_MethodToken + { + [TestMethod] + public void TestSerialize() + { + var result = new MethodToken() + { + CallFlags = CallFlags.AllowCall, + Hash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + Method = "myMethod", + ParametersCount = 123, + RVCount = 456 + }; + + var copy = result.ToArray().AsSerializable(); + + Assert.AreEqual(CallFlags.AllowCall, copy.CallFlags); + Assert.AreEqual("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", copy.Hash.ToString()); + Assert.AreEqual("myMethod", copy.Method); + Assert.AreEqual(123, copy.ParametersCount); + Assert.AreEqual(456, copy.RVCount); + } + + [TestMethod] + public void TestSerializeErrors() + { + var result = new MethodToken() + { + CallFlags = (CallFlags)byte.MaxValue, + Hash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + Method = "myLongMethod", + ParametersCount = 123, + RVCount = 456 + }; + + Assert.ThrowsException(() => result.ToArray().AsSerializable()); + + result.CallFlags = CallFlags.All; + result.Method += "-123123123123123123123123"; + Assert.ThrowsException(() => result.ToArray().AsSerializable()); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs b/tests/neo.UnitTests/SmartContract/UT_NefFile.cs index 7a5412d980..575b4a0549 100644 --- a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs +++ b/tests/neo.UnitTests/SmartContract/UT_NefFile.cs @@ -14,6 +14,7 @@ public class UT_NefFile { Compiler = "".PadLeft(32, ' '), Version = new Version(1, 2, 3, 4).ToString(), + Tokens = Array.Empty(), Script = new byte[] { 0x01, 0x02, 0x03 } }; @@ -78,7 +79,7 @@ public void TestDeserialize() [TestMethod] public void TestGetSize() { - file.Size.Should().Be(4 + 32 + 32 + 4 + 4); + file.Size.Should().Be(4 + 32 + 32 + 2 + 1 + 2 + 4 + 4); } [TestMethod] @@ -88,6 +89,7 @@ public void ParseTest() { Compiler = "".PadLeft(32, ' '), Version = new Version(1, 2, 3, 4).ToString(), + Tokens = Array.Empty(), Script = new byte[] { 0x01, 0x02, 0x03 } }; @@ -108,6 +110,7 @@ public void LimitTest() { Compiler = "".PadLeft(byte.MaxValue, ' '), Version = new Version(1, 2, 3, 4).ToString(), + Tokens = Array.Empty(), Script = new byte[1024 * 1024], CheckSum = 0 }; From 198e9a61b735bb304ab57915c940d7323d1f8d2d Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 28 Dec 2020 18:55:38 +0800 Subject: [PATCH 09/65] Add native contract base class: NonfungibleToken (#2177) --- src/neo/SmartContract/ApplicationEngine.cs | 2 + src/neo/SmartContract/Native/NFTState.cs | 39 ++++ .../SmartContract/Native/NonfungibleToken.cs | 196 ++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 src/neo/SmartContract/Native/NFTState.cs create mode 100644 src/neo/SmartContract/Native/NonfungibleToken.cs diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 96536a6639..3b84e85a50 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -1,4 +1,5 @@ using Neo.IO; +using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -159,6 +160,7 @@ protected internal StackItem Convert(object value) byte[] data => data, string s => s, BigInteger i => i, + JObject o => o.ToByteArray(false), IInteroperable interoperable => interoperable.ToStackItem(ReferenceCounter), ISerializable i => i.ToArray(), StackItem item => item, diff --git a/src/neo/SmartContract/Native/NFTState.cs b/src/neo/SmartContract/Native/NFTState.cs new file mode 100644 index 0000000000..cd01e8fbe5 --- /dev/null +++ b/src/neo/SmartContract/Native/NFTState.cs @@ -0,0 +1,39 @@ +using Neo.IO; +using Neo.IO.Json; +using Neo.VM; +using Neo.VM.Types; +using System.Linq; + +namespace Neo.SmartContract.Native +{ + public abstract class NFTState : IInteroperable + { + public UInt160 Owner; + public string Name; + public string Description; + + public abstract byte[] Id { get; } + + public virtual JObject ToJson() + { + return new JObject + { + ["name"] = Name, + ["description"] = Description + }; + } + + public virtual void FromStackItem(StackItem stackItem) + { + Struct @struct = (Struct)stackItem; + Owner = new UInt160(@struct[0].GetSpan()); + Name = @struct[1].GetString(); + Description = @struct[2].GetString(); + } + + public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) { Owner.ToArray(), Name, Description }; + } + } +} diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs new file mode 100644 index 0000000000..a5f4aec75c --- /dev/null +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -0,0 +1,196 @@ +using Neo.IO; +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Persistence; +using Neo.SmartContract.Enumerators; +using Neo.SmartContract.Iterators; +using Neo.SmartContract.Manifest; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract.Native +{ + public abstract class NonfungibleToken : NativeContract + where TokenState : NFTState, new() + { + [ContractMethod(0, CallFlags.None)] + public abstract string Symbol { get; } + [ContractMethod(0, CallFlags.None)] + public byte Decimals => 0; + + private const byte Prefix_TotalSupply = 11; + private const byte Prefix_Account = 7; + private const byte Prefix_Token = 5; + + protected NonfungibleToken() + { + var events = new List(Manifest.Abi.Events) + { + new ContractEventDescriptor() + { + Name = "Transfer", + Parameters = new ContractParameterDefinition[] + { + new ContractParameterDefinition() + { + Name = "from", + Type = ContractParameterType.Hash160 + }, + new ContractParameterDefinition() + { + Name = "to", + Type = ContractParameterType.Hash160 + }, + new ContractParameterDefinition() + { + Name = "amount", + Type = ContractParameterType.Integer + }, + new ContractParameterDefinition() + { + Name = "tokenId", + Type = ContractParameterType.ByteArray + } + } + } + }; + + Manifest.Abi.Events = events.ToArray(); + } + + protected void Mint(ApplicationEngine engine, TokenState token) + { + engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Token).Add(token.Id), new StorageItem(token)); + NFTAccountState account = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Account).Add(token.Owner), () => new StorageItem(new NFTAccountState())).GetInteroperable(); + account.Add(token.Id); + engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero)).Add(1); + PostTransfer(engine, null, token.Owner, token.Id); + } + + protected void Burn(ApplicationEngine engine, byte[] tokenId) + { + StorageKey key_token = CreateStorageKey(Prefix_Token).Add(tokenId); + TokenState token = engine.Snapshot.Storages.TryGet(key_token)?.GetInteroperable(); + if (token is null) throw new InvalidOperationException(); + engine.Snapshot.Storages.Delete(key_token); + StorageKey key_account = CreateStorageKey(Prefix_Account).Add(token.Owner); + NFTAccountState account = engine.Snapshot.Storages.GetAndChange(key_account).GetInteroperable(); + account.Remove(tokenId); + if (account.Balance.IsZero) + engine.Snapshot.Storages.Delete(key_account); + engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply)).Add(-1); + PostTransfer(engine, token.Owner, null, tokenId); + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public BigInteger TotalSupply(StoreView snapshot) + { + StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_TotalSupply)); + return storage ?? BigInteger.Zero; + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public UInt160 OwnerOf(StoreView snapshot, byte[] tokenId) + { + return snapshot.Storages[CreateStorageKey(Prefix_Token).Add(tokenId)].GetInteroperable().Owner; + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public JObject Properties(StoreView snapshot, byte[] tokenId) + { + return snapshot.Storages[CreateStorageKey(Prefix_Token).Add(tokenId)].GetInteroperable().ToJson(); + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public BigInteger BalanceOf(StoreView snapshot, UInt160 owner) + { + if (owner is null) throw new ArgumentNullException(nameof(owner)); + return snapshot.Storages.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable().Balance ?? BigInteger.Zero; + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public IEnumerator TokensOf(StoreView snapshot, UInt160 owner) + { + if (owner is null) + { + var keys = snapshot.Storages.Find(new[] { Prefix_Token }).Select(p => p.Key); + return new StorageKeyEnumerator(keys.GetEnumerator(), 1); + } + else + { + NFTAccountState account = snapshot.Storages.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable(); + IReadOnlyList tokens = account?.Tokens ?? (IReadOnlyList)System.Array.Empty(); + return new ArrayWrapper(tokens.Select(p => (StackItem)p).ToArray()); + } + } + + [ContractMethod(0_09000000, CallFlags.WriteStates | CallFlags.AllowNotify)] + protected bool Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) + { + if (to is null) throw new ArgumentNullException(nameof(to)); + TokenState token = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Token).Add(tokenId))?.GetInteroperable(); + if (token is null) throw new ArgumentException(); + UInt160 from = token.Owner; + if (!from.Equals(engine.CallingScriptHash) && !engine.CheckWitnessInternal(from)) + return false; + if (!from.Equals(to)) + { + StorageKey key_from = CreateStorageKey(Prefix_Account).Add(from); + NFTAccountState account = engine.Snapshot.Storages.GetAndChange(key_from).GetInteroperable(); + account.Remove(tokenId); + if (account.Balance.IsZero) + engine.Snapshot.Storages.Delete(key_from); + token.Owner = to; + StorageKey key_to = CreateStorageKey(Prefix_Account).Add(to); + account = engine.Snapshot.Storages.GetAndChange(key_to, () => new StorageItem(new NFTAccountState())).GetInteroperable(); + account.Add(tokenId); + } + PostTransfer(engine, from, to, tokenId); + return true; + } + + private void PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, byte[] tokenId) + { + engine.SendNotification(Hash, "Transfer", + new Array { from?.ToArray() ?? StackItem.Null, to?.ToArray() ?? StackItem.Null, 1, tokenId }); + } + + class NFTAccountState : AccountState + { + public readonly List Tokens = new List(); + + public void Add(byte[] tokenId) + { + Balance++; + int index = ~Tokens.BinarySearch(tokenId, ByteArrayComparer.Default); + Tokens.Insert(index, tokenId); + } + + public void Remove(byte[] tokenId) + { + Balance--; + int index = Tokens.BinarySearch(tokenId, ByteArrayComparer.Default); + Tokens.RemoveAt(index); + } + + public override void FromStackItem(StackItem stackItem) + { + base.FromStackItem(stackItem); + Array array = (Array)((Struct)stackItem)[1]; + Tokens.AddRange(array.Select(p => p.GetSpan().ToArray())); + } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + Struct @struct = (Struct)base.ToStackItem(referenceCounter); + @struct.Add(new Array(referenceCounter, Tokens.Select(p => (StackItem)p))); + return @struct; + } + } + } +} From 0e1188643650cad461c4a03557ce8647c069a4ed Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 29 Dec 2020 00:16:28 +0800 Subject: [PATCH 10/65] Rename Nep17Token to FungibleToken (#2187) --- .../SmartContract/Native/{Nep17Token.cs => FungibleToken.cs} | 4 ++-- src/neo/SmartContract/Native/GasToken.cs | 2 +- src/neo/SmartContract/Native/NativeContract.cs | 2 +- src/neo/SmartContract/Native/NeoToken.cs | 2 +- src/neo/SmartContract/Native/OracleContract.cs | 2 +- src/neo/SmartContract/Native/RoleManagement.cs | 2 +- .../Native/{UT_Nep17Token.cs => UT_FungibleToken.cs} | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) rename src/neo/SmartContract/Native/{Nep17Token.cs => FungibleToken.cs} (98%) rename tests/neo.UnitTests/SmartContract/Native/{UT_Nep17Token.cs => UT_FungibleToken.cs} (95%) diff --git a/src/neo/SmartContract/Native/Nep17Token.cs b/src/neo/SmartContract/Native/FungibleToken.cs similarity index 98% rename from src/neo/SmartContract/Native/Nep17Token.cs rename to src/neo/SmartContract/Native/FungibleToken.cs index df6f3e217e..3e000c61b7 100644 --- a/src/neo/SmartContract/Native/Nep17Token.cs +++ b/src/neo/SmartContract/Native/FungibleToken.cs @@ -10,7 +10,7 @@ namespace Neo.SmartContract.Native { - public abstract class Nep17Token : NativeContract + public abstract class FungibleToken : NativeContract where TState : AccountState, new() { [ContractMethod(0, CallFlags.None)] @@ -22,7 +22,7 @@ public abstract class Nep17Token : NativeContract protected const byte Prefix_TotalSupply = 11; protected const byte Prefix_Account = 20; - protected Nep17Token() + protected FungibleToken() { this.Factor = BigInteger.Pow(10, Decimals); diff --git a/src/neo/SmartContract/Native/GasToken.cs b/src/neo/SmartContract/Native/GasToken.cs index e747ede414..0a9c85de4d 100644 --- a/src/neo/SmartContract/Native/GasToken.cs +++ b/src/neo/SmartContract/Native/GasToken.cs @@ -4,7 +4,7 @@ namespace Neo.SmartContract.Native { - public sealed class GasToken : Nep17Token + public sealed class GasToken : FungibleToken { public override int Id => -2; public override string Symbol => "GAS"; diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index ad4adeef5c..375e0fb105 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -23,8 +23,8 @@ public abstract class NativeContract public static NeoToken NEO { get; } = new NeoToken(); public static GasToken GAS { get; } = new GasToken(); public static PolicyContract Policy { get; } = new PolicyContract(); - public static OracleContract Oracle { get; } = new OracleContract(); public static RoleManagement RoleManagement { get; } = new RoleManagement(); + public static OracleContract Oracle { get; } = new OracleContract(); public string Name => GetType().Name; public byte[] Script { get; } diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index 1a9fac5bf1..85a81afac6 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -15,7 +15,7 @@ namespace Neo.SmartContract.Native { - public sealed class NeoToken : Nep17Token + public sealed class NeoToken : FungibleToken { public override int Id => -1; public override string Symbol => "NEO"; diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index f619df4498..3f8f5e7567 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -28,7 +28,7 @@ public sealed class OracleContract : NativeContract private const long OracleRequestPrice = 0_50000000; - public override int Id => -4; + public override int Id => -5; internal OracleContract() { diff --git a/src/neo/SmartContract/Native/RoleManagement.cs b/src/neo/SmartContract/Native/RoleManagement.cs index 546969432f..728f67aab7 100644 --- a/src/neo/SmartContract/Native/RoleManagement.cs +++ b/src/neo/SmartContract/Native/RoleManagement.cs @@ -14,7 +14,7 @@ namespace Neo.SmartContract.Native { public sealed class RoleManagement : NativeContract { - public override int Id => -5; + public override int Id => -4; internal RoleManagement() { diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_Nep17Token.cs b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs similarity index 95% rename from tests/neo.UnitTests/SmartContract/Native/UT_Nep17Token.cs rename to tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs index 2f9bfe9837..8b57384f5e 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_Nep17Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs @@ -9,7 +9,7 @@ namespace Neo.UnitTests.SmartContract.Native { [TestClass] - public class UT_Nep17Token : TestKit + public class UT_FungibleToken : TestKit { [TestInitialize] public void TestSetup() @@ -77,7 +77,7 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null) } } - public class TestNep17Token : Nep17Token + public class TestNep17Token : FungibleToken { public override int Id => 0x10000005; public override string Symbol => throw new NotImplementedException(); From 52a912259cfa11a83cbe9cb075f5eb672737b423 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 30 Dec 2020 03:11:54 +0800 Subject: [PATCH 11/65] Fix disposables in ApplicationEngine (#2191) --- src/neo/SmartContract/ApplicationEngine.Storage.cs | 8 ++------ src/neo/SmartContract/ApplicationEngine.cs | 1 + 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index 2d542f7501..fb4d383822 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -65,17 +65,13 @@ protected internal byte[] Get(StorageContext context, byte[] key) protected internal IIterator Find(StorageContext context, byte[] prefix) { byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); - StorageIterator iterator = new StorageIterator(Snapshot.Storages.Find(prefix_key).GetEnumerator()); - Disposables.Add(iterator); - return iterator; + return new StorageIterator(Snapshot.Storages.Find(prefix_key).GetEnumerator()); } protected internal IEnumerator FindKeys(StorageContext context, byte[] prefix, byte removePrefix) { byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); - StorageKeyEnumerator enumerator = new StorageKeyEnumerator(Snapshot.Storages.Find(prefix_key).Select(p => p.Key).GetEnumerator(), removePrefix); - Disposables.Add(enumerator); - return enumerator; + return new StorageKeyEnumerator(Snapshot.Storages.Find(prefix_key).Select(p => p.Key).GetEnumerator(), removePrefix); } protected internal void Put(StorageContext context, byte[] key, byte[] value) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 3b84e85a50..3d1ca22659 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -144,6 +144,7 @@ public ExecutionContext LoadScript(Script script, ushort pcount = 0, int rvcount protected internal StackItem Convert(object value) { + if (value is IDisposable disposable) Disposables.Add(disposable); return value switch { null => StackItem.Null, From bb66fda24ec2640a526cb26141dfa2cf3234c52f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 30 Dec 2020 16:33:48 +0800 Subject: [PATCH 12/65] Optimize ApplicationEngine.LoadScript() (#2186) Co-authored-by: Shargon --- src/neo/SmartContract/ApplicationEngine.cs | 13 +++++++------ src/neo/SmartContract/ExecutionContextState.cs | 2 +- src/neo/SmartContract/Helper.cs | 8 ++++++-- .../Extensions/Nep17NativeContractExtensions.cs | 10 +++++----- .../SmartContract/Native/UT_NativeContract.cs | 2 +- .../SmartContract/Native/UT_NeoToken.cs | 14 +++++++------- tests/neo.UnitTests/SmartContract/UT_Contract.cs | 4 ++-- 7 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 3d1ca22659..d04c7e2b9b 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -117,8 +117,11 @@ public ExecutionContext LoadContract(ContractState contract, string method, Call pcount: pcount, rvcount: hasReturnValue ? 1 : 0, initialPosition: md.Offset, - callFlags: callFlags, - scriptHash: contract.Hash); + configureState: p => + { + p.CallFlags = callFlags; + p.ScriptHash = contract.Hash; + }); // Call initialization var init = contract.Manifest.Abi.GetMethod("_initialize"); @@ -130,13 +133,11 @@ public ExecutionContext LoadContract(ContractState contract, string method, Call return context; } - public ExecutionContext LoadScript(Script script, ushort pcount = 0, int rvcount = -1, int initialPosition = 0, CallFlags callFlags = CallFlags.All, UInt160 scriptHash = null) + public ExecutionContext LoadScript(Script script, ushort pcount = 0, int rvcount = -1, int initialPosition = 0, Action configureState = null) { // Create and configure context ExecutionContext context = CreateContext(script, pcount, rvcount, initialPosition); - var state = context.GetState(); - state.CallFlags = callFlags; - state.ScriptHash = scriptHash ?? ((byte[])script).ToScriptHash(); + configureState?.Invoke(context.GetState()); // Load context LoadContext(context); return context; diff --git a/src/neo/SmartContract/ExecutionContextState.cs b/src/neo/SmartContract/ExecutionContextState.cs index 7c5aca4231..0d0250803c 100644 --- a/src/neo/SmartContract/ExecutionContextState.cs +++ b/src/neo/SmartContract/ExecutionContextState.cs @@ -1,6 +1,6 @@ namespace Neo.SmartContract { - internal class ExecutionContextState + public class ExecutionContextState { /// /// Script hash diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index a84c9ec1c7..4c1a6a55a6 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -194,10 +194,14 @@ internal static bool VerifyWitness(this IVerifiable verifiable, StoreView snapsh { if (NativeContract.IsNative(hash)) return false; if (hash != witness.ScriptHash) return false; - engine.LoadScript(verification, callFlags: callFlags, scriptHash: hash, initialPosition: 0); + engine.LoadScript(verification, initialPosition: 0, configureState: p => + { + p.CallFlags = callFlags; + p.ScriptHash = hash; + }); } - engine.LoadScript(witness.InvocationScript, callFlags: CallFlags.None); + engine.LoadScript(witness.InvocationScript, configureState: p => p.CallFlags = CallFlags.None); if (NativeContract.IsNative(hash)) { diff --git a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs index 942993b5e6..909ae98d86 100644 --- a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs @@ -40,7 +40,7 @@ public static bool Transfer(this NativeContract contract, StoreView snapshot, by var engine = ApplicationEngine.Create(TriggerType.Application, new ManualWitness(signFrom ? new UInt160(from) : null), snapshot); - engine.LoadScript(contract.Script, pcount: 4, callFlags: CallFlags.All, scriptHash: contract.Hash); + engine.LoadScript(contract.Script, pcount: 4, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.Emit(OpCode.PUSHNULL); @@ -65,7 +65,7 @@ public static BigInteger TotalSupply(this NativeContract contract, StoreView sna { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: contract.Hash); + engine.LoadScript(contract.Script, pcount: 0, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.EmitPush("totalSupply"); @@ -83,7 +83,7 @@ public static BigInteger BalanceOf(this NativeContract contract, StoreView snaps { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, pcount: 1, callFlags: CallFlags.All, scriptHash: contract.Hash); + engine.LoadScript(contract.Script, pcount: 1, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.EmitPush(account); @@ -102,7 +102,7 @@ public static BigInteger Decimals(this NativeContract contract, StoreView snapsh { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: contract.Hash); + engine.LoadScript(contract.Script, pcount: 0, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.EmitPush("decimals"); @@ -120,7 +120,7 @@ public static string Symbol(this NativeContract contract, StoreView snapshot) { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: contract.Hash); + engine.LoadScript(contract.Script, pcount: 0, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.EmitPush("symbol"); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index f7ccdd206d..c473829357 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -99,7 +99,7 @@ public void TestInvoke() { var snapshot = Blockchain.Singleton.GetSnapshot(); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, 0); - engine.LoadScript(testNativeContract.Script, callFlags: CallFlags.All, scriptHash: testNativeContract.Hash); + engine.LoadScript(testNativeContract.Script, configureState: p => p.ScriptHash = testNativeContract.Hash); ByteString method1 = new ByteString(System.Text.Encoding.Default.GetBytes("wrongMethod")); VMArray args1 = new VMArray(); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 02899a5a71..1bf90d305d 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -867,7 +867,7 @@ internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView sn { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(NativeContract.NEO.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 0, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush("getGasPerBlock"); @@ -889,7 +889,7 @@ internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreV UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot); - engine.LoadScript(NativeContract.NEO.Script, pcount: 1, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush(gasPerBlock); @@ -912,7 +912,7 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot); - engine.LoadScript(NativeContract.NEO.Script, pcount: 2, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 2, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); var script = new ScriptBuilder(); @@ -940,7 +940,7 @@ internal static (bool State, bool Result) Check_RegisterValidator(StoreView snap var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot); - engine.LoadScript(NativeContract.NEO.Script, pcount: 1, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush(pubkey); @@ -962,7 +962,7 @@ internal static ECPoint[] Check_GetCommittee(StoreView snapshot) { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(NativeContract.NEO.Script, pcount: 0, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 0, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush("getCommittee"); @@ -980,7 +980,7 @@ internal static (BigInteger Value, bool State) Check_UnclaimedGas(StoreView snap { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(NativeContract.NEO.Script, pcount: 2, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 2, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush(snapshot.PersistingBlock.Index); @@ -1040,7 +1040,7 @@ internal static (bool State, bool Result) Check_UnregisterCandidate(StoreView sn var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot); - engine.LoadScript(NativeContract.NEO.Script, pcount: 1, callFlags: CallFlags.All, scriptHash: NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush(pubkey); diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs index 57cea86681..cc67603991 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Contract.cs @@ -180,7 +180,7 @@ public void TestSignatureRedeemScriptFee() using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null)) { - engine.LoadScript(invocation.Concat(verification).ToArray(), callFlags: CallFlags.None); + engine.LoadScript(invocation.Concat(verification).ToArray(), configureState: p => p.CallFlags = CallFlags.None); engine.Execute(); engine.GasConsumed.Should().Be(fee); } @@ -208,7 +208,7 @@ public void TestCreateMultiSigRedeemScriptFee() using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null)) { - engine.LoadScript(invocation.Concat(verification).ToArray(), callFlags: CallFlags.None); + engine.LoadScript(invocation.Concat(verification).ToArray(), configureState: p => p.CallFlags = CallFlags.None); engine.Execute(); engine.GasConsumed.Should().Be(fee); } From 6c5d44a50672e036cf9e2a7573023ba2f8fde62d Mon Sep 17 00:00:00 2001 From: Luchuan Date: Wed, 30 Dec 2020 16:48:05 +0800 Subject: [PATCH 13/65] fix tx.verify (#2193) Co-authored-by: Tommo-L Co-authored-by: Shargon --- src/neo/Network/P2P/Payloads/Transaction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 7ea9c8e43f..77252f04c6 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -308,7 +308,7 @@ public virtual VerifyResult VerifyStateDependent(StoreView snapshot, Transaction net_fee -= execFeeFactor * SmartContract.Helper.MultiSignatureContractCost(m, n); else { - if (!this.VerifyWitness(null, hashes[i], witnesses[i], net_fee, out long fee)) + if (!this.VerifyWitness(snapshot, hashes[i], witnesses[i], net_fee, out long fee)) return VerifyResult.Invalid; net_fee -= fee; } From 59ea84b6d1eca9924adb7b55cb3456488d5e58d4 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 30 Dec 2020 16:55:21 +0800 Subject: [PATCH 14/65] Allow call native contracts with TriggerType.Verification (#2194) --- src/neo/SmartContract/ApplicationEngine.Contract.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 5f274359cf..7e9f9e9c1b 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -85,7 +85,7 @@ private void CallContractInternal(ContractState contract, ContractMethodDescript protected internal void CallNativeContract(string name) { NativeContract contract = NativeContract.GetContract(name); - if (contract is null || contract.ActiveBlockIndex > Snapshot.PersistingBlock.Index) + if (contract is null || contract.ActiveBlockIndex > Snapshot.Height) throw new InvalidOperationException(); contract.Invoke(this); } From 8f98adaff895188d19154326ab9ce570697122df Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 3 Jan 2021 18:19:42 +0800 Subject: [PATCH 15/65] Simplify Iterator APIs (#2190) Co-authored-by: Shargon --- .../ApplicationEngine.Enumerator.cs | 36 ---------- .../ApplicationEngine.Iterator.cs | 21 ++---- .../ApplicationEngine.Storage.cs | 18 +++-- .../SmartContract/Enumerators/IEnumerator.cs | 11 --- .../Enumerators/IteratorKeysWrapper.cs | 30 -------- .../Enumerators/IteratorValuesWrapper.cs | 30 -------- .../Enumerators/StorageKeyEnumerator.cs | 36 ---------- src/neo/SmartContract/FindOptions.cs | 17 +++++ .../SmartContract/Iterators/ArrayWrapper.cs | 7 -- .../Iterators/ByteArrayWrapper.cs | 7 -- src/neo/SmartContract/Iterators/IIterator.cs | 7 +- src/neo/SmartContract/Iterators/MapWrapper.cs | 12 ++-- .../Iterators/StorageIterator.cs | 33 +++++++-- .../SmartContract/Native/NonfungibleToken.cs | 7 +- .../Enumerators/UT_IteratorKeysWrapper.cs | 36 ---------- .../Enumerators/UT_IteratorValuesWrapper.cs | 37 ---------- .../Enumerators/UT_StorageKeyEnumerator.cs | 69 ------------------- .../Iterators/UT_ArrayWrapper.cs | 3 - .../SmartContract/Iterators/UT_MapWrapper.cs | 11 +-- .../Iterators/UT_PrimitiveWrapper.cs | 4 -- .../Iterators/UT_StorageIterator.cs | 6 +- .../SmartContract/UT_InteropService.NEO.cs | 69 +++---------------- 22 files changed, 87 insertions(+), 420 deletions(-) delete mode 100644 src/neo/SmartContract/ApplicationEngine.Enumerator.cs delete mode 100644 src/neo/SmartContract/Enumerators/IEnumerator.cs delete mode 100644 src/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs delete mode 100644 src/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs delete mode 100644 src/neo/SmartContract/Enumerators/StorageKeyEnumerator.cs create mode 100644 src/neo/SmartContract/FindOptions.cs delete mode 100644 tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs delete mode 100644 tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs delete mode 100644 tests/neo.UnitTests/SmartContract/Enumerators/UT_StorageKeyEnumerator.cs diff --git a/src/neo/SmartContract/ApplicationEngine.Enumerator.cs b/src/neo/SmartContract/ApplicationEngine.Enumerator.cs deleted file mode 100644 index f0e5839651..0000000000 --- a/src/neo/SmartContract/ApplicationEngine.Enumerator.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Neo.SmartContract.Enumerators; -using Neo.SmartContract.Iterators; -using Neo.VM.Types; -using System; -using Array = Neo.VM.Types.Array; - -namespace Neo.SmartContract -{ - partial class ApplicationEngine - { - public static readonly InteropDescriptor System_Enumerator_Create = Register("System.Enumerator.Create", nameof(CreateEnumerator), 1 << 4, CallFlags.None); - public static readonly InteropDescriptor System_Enumerator_Next = Register("System.Enumerator.Next", nameof(EnumeratorNext), 1 << 15, CallFlags.None); - public static readonly InteropDescriptor System_Enumerator_Value = Register("System.Enumerator.Value", nameof(EnumeratorValue), 1 << 4, CallFlags.None); - - protected internal IEnumerator CreateEnumerator(StackItem item) - { - return item switch - { - Array array => new ArrayWrapper(array), - VM.Types.Buffer buffer => new ByteArrayWrapper(buffer), - PrimitiveType primitive => new ByteArrayWrapper(primitive), - _ => throw new ArgumentException() - }; - } - - protected internal bool EnumeratorNext(IEnumerator enumerator) - { - return enumerator.Next(); - } - - protected internal StackItem EnumeratorValue(IEnumerator enumerator) - { - return enumerator.Value(); - } - } -} diff --git a/src/neo/SmartContract/ApplicationEngine.Iterator.cs b/src/neo/SmartContract/ApplicationEngine.Iterator.cs index 685d195f2a..f8f6be2af7 100644 --- a/src/neo/SmartContract/ApplicationEngine.Iterator.cs +++ b/src/neo/SmartContract/ApplicationEngine.Iterator.cs @@ -1,4 +1,3 @@ -using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; using Neo.VM.Types; using System; @@ -9,35 +8,29 @@ namespace Neo.SmartContract partial class ApplicationEngine { public static readonly InteropDescriptor System_Iterator_Create = Register("System.Iterator.Create", nameof(CreateIterator), 1 << 4, CallFlags.None); - public static readonly InteropDescriptor System_Iterator_Key = Register("System.Iterator.Key", nameof(IteratorKey), 1 << 4, CallFlags.None); - public static readonly InteropDescriptor System_Iterator_Keys = Register("System.Iterator.Keys", nameof(IteratorKeys), 1 << 4, CallFlags.None); - public static readonly InteropDescriptor System_Iterator_Values = Register("System.Iterator.Values", nameof(IteratorValues), 1 << 4, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Next = Register("System.Iterator.Next", nameof(IteratorNext), 1 << 15, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Value = Register("System.Iterator.Value", nameof(IteratorValue), 1 << 4, CallFlags.None); protected internal IIterator CreateIterator(StackItem item) { return item switch { Array array => new ArrayWrapper(array), - Map map => new MapWrapper(map), + Map map => new MapWrapper(map, ReferenceCounter), VM.Types.Buffer buffer => new ByteArrayWrapper(buffer), PrimitiveType primitive => new ByteArrayWrapper(primitive), _ => throw new ArgumentException() }; } - protected internal PrimitiveType IteratorKey(IIterator iterator) + protected internal bool IteratorNext(IIterator iterator) { - return iterator.Key(); + return iterator.Next(); } - protected internal IEnumerator IteratorKeys(IIterator iterator) + protected internal StackItem IteratorValue(IIterator iterator) { - return new IteratorKeysWrapper(iterator); - } - - protected internal IEnumerator IteratorValues(IIterator iterator) - { - return new IteratorValuesWrapper(iterator); + return iterator.Value(); } } } diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index fb4d383822..a27edd2582 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -1,5 +1,4 @@ using Neo.Ledger; -using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; using Neo.SmartContract.Native; using System; @@ -17,7 +16,6 @@ partial class ApplicationEngine public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 1 << 4, CallFlags.ReadStates); public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 1 << 15, CallFlags.ReadStates); public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 1 << 15, CallFlags.ReadStates); - public static readonly InteropDescriptor System_Storage_FindKeys = Register("System.Storage.FindKeys", nameof(FindKeys), 1 << 15, CallFlags.ReadStates); public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, CallFlags.WriteStates); public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, CallFlags.WriteStates); public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 0, CallFlags.WriteStates); @@ -62,16 +60,16 @@ protected internal byte[] Get(StorageContext context, byte[] key) })?.Value; } - protected internal IIterator Find(StorageContext context, byte[] prefix) - { - byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); - return new StorageIterator(Snapshot.Storages.Find(prefix_key).GetEnumerator()); - } - - protected internal IEnumerator FindKeys(StorageContext context, byte[] prefix, byte removePrefix) + protected internal IIterator Find(StorageContext context, byte[] prefix, FindOptions options) { + if (options.HasFlag(FindOptions.KeysOnly) && options.HasFlag(FindOptions.ValuesOnly)) + throw new ArgumentException(); + if (options.HasFlag(FindOptions.PickField0) && options.HasFlag(FindOptions.PickField1)) + throw new ArgumentException(); + if ((options.HasFlag(FindOptions.PickField0) || options.HasFlag(FindOptions.PickField1)) && !options.HasFlag(FindOptions.DeserializeValues)) + throw new ArgumentException(); byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); - return new StorageKeyEnumerator(Snapshot.Storages.Find(prefix_key).Select(p => p.Key).GetEnumerator(), removePrefix); + return new StorageIterator(Snapshot.Storages.Find(prefix_key).GetEnumerator(), options, ReferenceCounter); } protected internal void Put(StorageContext context, byte[] key, byte[] value) diff --git a/src/neo/SmartContract/Enumerators/IEnumerator.cs b/src/neo/SmartContract/Enumerators/IEnumerator.cs deleted file mode 100644 index 77da74bd43..0000000000 --- a/src/neo/SmartContract/Enumerators/IEnumerator.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Neo.VM.Types; -using System; - -namespace Neo.SmartContract.Enumerators -{ - public interface IEnumerator : IDisposable - { - bool Next(); - StackItem Value(); - } -} diff --git a/src/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs b/src/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs deleted file mode 100644 index a7a7f040a4..0000000000 --- a/src/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Neo.SmartContract.Iterators; -using Neo.VM.Types; - -namespace Neo.SmartContract.Enumerators -{ - internal class IteratorKeysWrapper : IEnumerator - { - private readonly IIterator iterator; - - public IteratorKeysWrapper(IIterator iterator) - { - this.iterator = iterator; - } - - public void Dispose() - { - iterator.Dispose(); - } - - public bool Next() - { - return iterator.Next(); - } - - public StackItem Value() - { - return iterator.Key(); - } - } -} diff --git a/src/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs b/src/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs deleted file mode 100644 index 5e285938cf..0000000000 --- a/src/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Neo.SmartContract.Iterators; -using Neo.VM.Types; - -namespace Neo.SmartContract.Enumerators -{ - internal class IteratorValuesWrapper : IEnumerator - { - private readonly IIterator iterator; - - public IteratorValuesWrapper(IIterator iterator) - { - this.iterator = iterator; - } - - public void Dispose() - { - iterator.Dispose(); - } - - public bool Next() - { - return iterator.Next(); - } - - public StackItem Value() - { - return iterator.Value(); - } - } -} diff --git a/src/neo/SmartContract/Enumerators/StorageKeyEnumerator.cs b/src/neo/SmartContract/Enumerators/StorageKeyEnumerator.cs deleted file mode 100644 index 3bb5b4e9db..0000000000 --- a/src/neo/SmartContract/Enumerators/StorageKeyEnumerator.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Neo.Ledger; -using Neo.VM.Types; -using System.Collections.Generic; - -namespace Neo.SmartContract.Enumerators -{ - internal class StorageKeyEnumerator : IEnumerator - { - private readonly IEnumerator enumerator; - private readonly byte removePrefix; - - public StorageKeyEnumerator(IEnumerator enumerator, byte removePrefix) - { - this.enumerator = enumerator; - this.removePrefix = removePrefix; - } - - public void Dispose() - { - enumerator.Dispose(); - } - - public bool Next() - { - return enumerator.MoveNext(); - } - - public StackItem Value() - { - byte[] key = enumerator.Current.Key; - if (removePrefix > 0) - key = key[removePrefix..]; - return key; - } - } -} diff --git a/src/neo/SmartContract/FindOptions.cs b/src/neo/SmartContract/FindOptions.cs new file mode 100644 index 0000000000..5c42929ae3 --- /dev/null +++ b/src/neo/SmartContract/FindOptions.cs @@ -0,0 +1,17 @@ +using System; + +namespace Neo.SmartContract +{ + [Flags] + public enum FindOptions : byte + { + None = 0, + + KeysOnly = 1 << 0, + RemovePrefix = 1 << 1, + ValuesOnly = 1 << 2, + DeserializeValues = 1 << 3, + PickField0 = 1 << 4, + PickField1 = 1 << 5 + } +} diff --git a/src/neo/SmartContract/Iterators/ArrayWrapper.cs b/src/neo/SmartContract/Iterators/ArrayWrapper.cs index c010466c99..aa460df093 100644 --- a/src/neo/SmartContract/Iterators/ArrayWrapper.cs +++ b/src/neo/SmartContract/Iterators/ArrayWrapper.cs @@ -18,13 +18,6 @@ public void Dispose() { } - public PrimitiveType Key() - { - if (index < 0) - throw new InvalidOperationException(); - return index; - } - public bool Next() { int next = index + 1; diff --git a/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs b/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs index c26ca2b0e1..2750def603 100644 --- a/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs +++ b/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs @@ -20,13 +20,6 @@ public ByteArrayWrapper(VM.Types.Buffer value) public void Dispose() { } - public PrimitiveType Key() - { - if (index < 0) - throw new InvalidOperationException(); - return index; - } - public bool Next() { int next = index + 1; diff --git a/src/neo/SmartContract/Iterators/IIterator.cs b/src/neo/SmartContract/Iterators/IIterator.cs index c5c866891a..d131b75977 100644 --- a/src/neo/SmartContract/Iterators/IIterator.cs +++ b/src/neo/SmartContract/Iterators/IIterator.cs @@ -1,10 +1,11 @@ -using Neo.SmartContract.Enumerators; using Neo.VM.Types; +using System; namespace Neo.SmartContract.Iterators { - public interface IIterator : IEnumerator + public interface IIterator : IDisposable { - PrimitiveType Key(); + bool Next(); + StackItem Value(); } } diff --git a/src/neo/SmartContract/Iterators/MapWrapper.cs b/src/neo/SmartContract/Iterators/MapWrapper.cs index 783993a43c..17dbb394d4 100644 --- a/src/neo/SmartContract/Iterators/MapWrapper.cs +++ b/src/neo/SmartContract/Iterators/MapWrapper.cs @@ -1,3 +1,4 @@ +using Neo.VM; using Neo.VM.Types; using System.Collections.Generic; @@ -6,10 +7,12 @@ namespace Neo.SmartContract.Iterators internal class MapWrapper : IIterator { private readonly IEnumerator> enumerator; + private readonly ReferenceCounter referenceCounter; - public MapWrapper(IEnumerable> map) + public MapWrapper(IEnumerable> map, ReferenceCounter referenceCounter) { this.enumerator = map.GetEnumerator(); + this.referenceCounter = referenceCounter; } public void Dispose() @@ -17,11 +20,6 @@ public void Dispose() enumerator.Dispose(); } - public PrimitiveType Key() - { - return enumerator.Current.Key; - } - public bool Next() { return enumerator.MoveNext(); @@ -29,7 +27,7 @@ public bool Next() public StackItem Value() { - return enumerator.Current.Value; + return new Struct(referenceCounter) { enumerator.Current.Key, enumerator.Current.Value }; } } } diff --git a/src/neo/SmartContract/Iterators/StorageIterator.cs b/src/neo/SmartContract/Iterators/StorageIterator.cs index a42875e1aa..bcca51b471 100644 --- a/src/neo/SmartContract/Iterators/StorageIterator.cs +++ b/src/neo/SmartContract/Iterators/StorageIterator.cs @@ -1,4 +1,5 @@ using Neo.Ledger; +using Neo.VM; using Neo.VM.Types; using System.Collections.Generic; @@ -7,10 +8,14 @@ namespace Neo.SmartContract.Iterators internal class StorageIterator : IIterator { private readonly IEnumerator<(StorageKey Key, StorageItem Value)> enumerator; + private readonly FindOptions options; + private readonly ReferenceCounter referenceCounter; - public StorageIterator(IEnumerator<(StorageKey, StorageItem)> enumerator) + public StorageIterator(IEnumerator<(StorageKey, StorageItem)> enumerator, FindOptions options, ReferenceCounter referenceCounter) { this.enumerator = enumerator; + this.options = options; + this.referenceCounter = referenceCounter; } public void Dispose() @@ -18,11 +23,6 @@ public void Dispose() enumerator.Dispose(); } - public PrimitiveType Key() - { - return enumerator.Current.Key.Key; - } - public bool Next() { return enumerator.MoveNext(); @@ -30,7 +30,26 @@ public bool Next() public StackItem Value() { - return enumerator.Current.Value.Value; + byte[] key = enumerator.Current.Key.Key; + byte[] value = enumerator.Current.Value.Value; + + if (options.HasFlag(FindOptions.RemovePrefix)) + key = key[1..]; + + StackItem item = options.HasFlag(FindOptions.DeserializeValues) + ? BinarySerializer.Deserialize(value, 1024, (uint)value.Length, referenceCounter) + : value; + + if (options.HasFlag(FindOptions.PickField0)) + item = ((Array)item)[0]; + else if (options.HasFlag(FindOptions.PickField1)) + item = ((Array)item)[1]; + + if (options.HasFlag(FindOptions.KeysOnly)) + return key; + if (options.HasFlag(FindOptions.ValuesOnly)) + return item; + return new Struct(referenceCounter) { key, item }; } } } diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index a5f4aec75c..45969da8b5 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -2,7 +2,6 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.Persistence; -using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; using Neo.SmartContract.Manifest; using Neo.VM; @@ -114,12 +113,12 @@ public BigInteger BalanceOf(StoreView snapshot, UInt160 owner) } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public IEnumerator TokensOf(StoreView snapshot, UInt160 owner) + public IIterator TokensOf(StoreView snapshot, UInt160 owner) { if (owner is null) { - var keys = snapshot.Storages.Find(new[] { Prefix_Token }).Select(p => p.Key); - return new StorageKeyEnumerator(keys.GetEnumerator(), 1); + var results = snapshot.Storages.Find(new[] { Prefix_Token }).GetEnumerator(); + return new StorageIterator(results, FindOptions.ValuesOnly | FindOptions.DeserializeValues | FindOptions.PickField1, null); } else { diff --git a/tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs b/tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs deleted file mode 100644 index 12b82be599..0000000000 --- a/tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract.Enumerators; -using Neo.SmartContract.Iterators; -using Neo.VM.Types; -using System; -using System.Collections.Generic; - -namespace Neo.UnitTests.SmartContract.Enumerators -{ - [TestClass] - public class UT_IteratorKeysWrapper - { - [TestMethod] - public void TestGeneratorAndDispose() - { - IteratorKeysWrapper iteratorKeysWrapper = new IteratorKeysWrapper(new ArrayWrapper(new List())); - Assert.IsNotNull(iteratorKeysWrapper); - Action action = () => iteratorKeysWrapper.Dispose(); - action.Should().NotThrow(); - } - - [TestMethod] - public void TestNextAndValue() - { - StackItem stackItem = new VM.Types.Boolean(true); - List list = new List(); - list.Add(stackItem); - ArrayWrapper wrapper = new ArrayWrapper(list); - IteratorKeysWrapper iteratorKeysWrapper = new IteratorKeysWrapper(wrapper); - Action action = () => iteratorKeysWrapper.Next(); - action.Should().NotThrow(); - Assert.AreEqual(new VM.Types.Integer(0), iteratorKeysWrapper.Value()); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs b/tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs deleted file mode 100644 index 2e8bf5709b..0000000000 --- a/tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs +++ /dev/null @@ -1,37 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract.Enumerators; -using Neo.SmartContract.Iterators; -using Neo.VM.Types; -using System; -using System.Collections.Generic; - -namespace Neo.UnitTests.SmartContract.Enumerators -{ - - [TestClass] - public class UT_IteratorValuesWrapper - { - [TestMethod] - public void TestGeneratorAndDispose() - { - IteratorValuesWrapper iteratorValuesWrapper = new IteratorValuesWrapper(new ArrayWrapper(new List())); - Assert.IsNotNull(iteratorValuesWrapper); - Action action = () => iteratorValuesWrapper.Dispose(); - action.Should().NotThrow(); - } - - [TestMethod] - public void TestNextAndValue() - { - StackItem stackItem = new VM.Types.Boolean(true); - List list = new List(); - list.Add(stackItem); - ArrayWrapper wrapper = new ArrayWrapper(list); - IteratorValuesWrapper iteratorValuesWrapper = new IteratorValuesWrapper(wrapper); - Action action = () => iteratorValuesWrapper.Next(); - action.Should().NotThrow(); - Assert.AreEqual(stackItem, iteratorValuesWrapper.Value()); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/Enumerators/UT_StorageKeyEnumerator.cs b/tests/neo.UnitTests/SmartContract/Enumerators/UT_StorageKeyEnumerator.cs deleted file mode 100644 index 6a69e8638c..0000000000 --- a/tests/neo.UnitTests/SmartContract/Enumerators/UT_StorageKeyEnumerator.cs +++ /dev/null @@ -1,69 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Ledger; -using Neo.SmartContract.Enumerators; -using System; -using System.Collections.Generic; - -namespace Neo.UnitTests.SmartContract.Enumerators -{ - [TestClass] - public class UT_StorageKeyEnumerator - { - private class TestEnumeratorDispose : IEnumerator - { - public bool IsDisposed { get; private set; } - public StorageKey Current => throw new NotImplementedException(); - object System.Collections.IEnumerator.Current => throw new NotImplementedException(); - public void Dispose() - { - IsDisposed = true; - } - public bool MoveNext() => throw new NotImplementedException(); - public void Reset() => throw new NotImplementedException(); - } - - [TestMethod] - public void TestGeneratorAndDispose() - { - var enumerator = new TestEnumeratorDispose(); - var iterator = new StorageKeyEnumerator(enumerator, 0); - Action action = () => iterator.Dispose(); - enumerator.IsDisposed.Should().BeFalse(); - action.Should().NotThrow(); - enumerator.IsDisposed.Should().BeTrue(); - } - - [TestMethod] - public void TestNextAndValue() - { - var list = new List - { - new StorageKey() { Id = 1, Key = new byte[] { 1, 2, 3 } }, - new StorageKey() { Id = 1, Key = new byte[] { 4, 5, 6 } } - }; - - // With prefix - - var iterator = new StorageKeyEnumerator(list.GetEnumerator(), 0); - Action actionTrue = () => iterator.Next().Should().BeTrue(); - actionTrue.Should().NotThrow(); - CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, iterator.Value().GetSpan().ToArray()); - actionTrue.Should().NotThrow(); - CollectionAssert.AreEqual(new byte[] { 4, 5, 6 }, iterator.Value().GetSpan().ToArray()); - Action actionFalse = () => iterator.Next().Should().BeFalse(); - actionFalse.Should().NotThrow(); - - // Without prefix - - iterator = new StorageKeyEnumerator(list.GetEnumerator(), 2); - actionTrue = () => iterator.Next().Should().BeTrue(); - actionTrue.Should().NotThrow(); - CollectionAssert.AreEqual(new byte[] { 3 }, iterator.Value().GetSpan().ToArray()); - actionTrue.Should().NotThrow(); - CollectionAssert.AreEqual(new byte[] { 6 }, iterator.Value().GetSpan().ToArray()); - actionFalse = () => iterator.Next().Should().BeFalse(); - actionFalse.Should().NotThrow(); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs index c997bb7fd6..18e2463c40 100644 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs +++ b/tests/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs @@ -26,12 +26,9 @@ public void TestKeyAndValue() StackItem stackItem = new Integer(0); list.Add(stackItem); ArrayWrapper arrayWrapper = new ArrayWrapper(list); - Action action1 = () => arrayWrapper.Key(); - action1.Should().Throw(); Action action2 = () => arrayWrapper.Value(); action2.Should().Throw(); arrayWrapper.Next(); - Assert.AreEqual(stackItem, arrayWrapper.Key()); Assert.AreEqual(stackItem, arrayWrapper.Value()); } diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs index 7ce7c7e845..7f7d663f72 100644 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs +++ b/tests/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs @@ -13,7 +13,7 @@ public class UT_MapWrapper [TestMethod] public void TestGeneratorAndDispose() { - MapWrapper mapWrapper = new MapWrapper(new List>()); + MapWrapper mapWrapper = new MapWrapper(new List>(), null); Assert.IsNotNull(mapWrapper); Action action = () => mapWrapper.Dispose(); action.Should().NotThrow(); @@ -26,16 +26,17 @@ public void TestKeyAndValue() Integer stackItem1 = new Integer(0); StackItem stackItem2 = new Integer(1); list.Add(new KeyValuePair(stackItem1, stackItem2)); - MapWrapper mapWrapper = new MapWrapper(list); + MapWrapper mapWrapper = new MapWrapper(list, null); mapWrapper.Next(); - Assert.AreEqual(stackItem1, mapWrapper.Key()); - Assert.AreEqual(stackItem2, mapWrapper.Value()); + Struct @struct = (Struct)mapWrapper.Value(); + Assert.AreEqual(stackItem1, @struct[0]); + Assert.AreEqual(stackItem2, @struct[1]); } [TestMethod] public void TestNext() { - MapWrapper mapWrapper = new MapWrapper(new List>()); + MapWrapper mapWrapper = new MapWrapper(new List>(), null); Assert.AreEqual(false, mapWrapper.Next()); } } diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs index c097986de3..58b48e9503 100644 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs +++ b/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs @@ -22,15 +22,11 @@ public void TestGeneratorAndDispose() public void TestKeyAndValue() { ByteArrayWrapper arrayWrapper = new ByteArrayWrapper(new byte[] { 0x01, 0x02 }); - Action action1 = () => arrayWrapper.Key(); - action1.Should().Throw(); Action action2 = () => arrayWrapper.Value(); action2.Should().Throw(); arrayWrapper.Next(); - Assert.AreEqual(0x00, arrayWrapper.Key().GetInteger()); Assert.AreEqual(0x01, arrayWrapper.Value()); arrayWrapper.Next(); - Assert.AreEqual(0x01, arrayWrapper.Key().GetInteger()); Assert.AreEqual(0x02, arrayWrapper.Value()); } diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs index 72cb823d70..806c4d4817 100644 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs +++ b/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; +using Neo.SmartContract; using Neo.SmartContract.Iterators; using Neo.VM.Types; using System; @@ -14,7 +15,7 @@ public class UT_StorageIterator [TestMethod] public void TestGeneratorAndDispose() { - StorageIterator storageIterator = new StorageIterator(new List<(StorageKey, StorageItem)>().GetEnumerator()); + StorageIterator storageIterator = new StorageIterator(new List<(StorageKey, StorageItem)>().GetEnumerator(), FindOptions.None, null); Assert.IsNotNull(storageIterator); Action action = () => storageIterator.Dispose(); action.Should().NotThrow(); @@ -29,9 +30,8 @@ public void TestKeyAndValueAndNext() StorageItem storageItem = new StorageItem(); storageItem.Value = new byte[1]; list.Add((storageKey, storageItem)); - StorageIterator storageIterator = new StorageIterator(list.GetEnumerator()); + StorageIterator storageIterator = new StorageIterator(list.GetEnumerator(), FindOptions.ValuesOnly, null); storageIterator.Next(); - Assert.AreEqual(new ByteString(new byte[1]), storageIterator.Key()); Assert.AreEqual(new ByteString(new byte[1]), storageIterator.Value()); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 73146fefbd..98cfcc1f14 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -285,38 +285,25 @@ public void TestStorage_Find() { Id = state.Id, IsReadOnly = false - }, new byte[] { 0x01 }); + }, new byte[] { 0x01 }, FindOptions.ValuesOnly); iterator.Next(); var ele = iterator.Value(); ele.GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); } [TestMethod] - public void TestEnumerator_Create() + public void TestIterator_Next() { var engine = GetEngine(); var arr = new VMArray { new byte[]{ 0x01 }, new byte[]{ 0x02 } }; - var ret = engine.CreateEnumerator(arr); - ret.Next(); - ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); - } - - [TestMethod] - public void TestEnumerator_Next() - { - var engine = GetEngine(); - var arr = new VMArray { - new byte[]{ 0x01 }, - new byte[]{ 0x02 } - }; - engine.EnumeratorNext(new ArrayWrapper(arr)).Should().BeTrue(); + engine.IteratorNext(new ArrayWrapper(arr)).Should().BeTrue(); } [TestMethod] - public void TestEnumerator_Value() + public void TestIterator_Value() { var engine = GetEngine(); var arr = new VMArray { @@ -325,7 +312,7 @@ public void TestEnumerator_Value() }; var wrapper = new ArrayWrapper(arr); wrapper.Next(); - engine.EnumeratorValue(wrapper).GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + engine.IteratorValue(wrapper).GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } [TestMethod] @@ -350,49 +337,9 @@ public void TestIterator_Create() }; ret = engine.CreateIterator(map); ret.Next(); - ret.Key().GetInteger().Should().Be(1); - ret.Value().GetInteger().Should().Be(2); - } - - [TestMethod] - public void TestIterator_Key() - { - var engine = GetEngine(); - var arr = new VMArray { - new byte[]{ 0x01 }, - new byte[]{ 0x02 } - }; - var wrapper = new ArrayWrapper(arr); - wrapper.Next(); - engine.IteratorKey(wrapper).GetInteger().Should().Be(0); - } - - [TestMethod] - public void TestIterator_Keys() - { - var engine = GetEngine(); - var arr = new VMArray { - new byte[]{ 0x01 }, - new byte[]{ 0x02 } - }; - var wrapper = new ArrayWrapper(arr); - var ret = engine.IteratorKeys(wrapper); - ret.Next(); - ret.Value().GetInteger().Should().Be(0); - } - - [TestMethod] - public void TestIterator_Values() - { - var engine = GetEngine(); - var arr = new VMArray { - new byte[]{ 0x01 }, - new byte[]{ 0x02 } - }; - var wrapper = new ArrayWrapper(arr); - var ret = engine.IteratorValues(wrapper); - ret.Next(); - ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + Struct @struct = (Struct)ret.Value(); + @struct[0].GetInteger().Should().Be(1); + @struct[1].GetInteger().Should().Be(2); } [TestMethod] From a054d6c14e3b82108d6e93438f52146d47cd9bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Mon, 4 Jan 2021 06:32:48 -0300 Subject: [PATCH 16/65] Fix name of method on ContractManagement (#2198) * Fix name of method on ContractManagement * Fix Co-authored-by: Shargon --- src/neo/SmartContract/Native/ContractManagement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 33f0802ffe..10ecf0d2ea 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -50,7 +50,7 @@ internal ContractManagement() }, new ContractEventDescriptor { - Name = "Destory", + Name = "Destroy", Parameters = new ContractParameterDefinition[] { new ContractParameterDefinition() @@ -213,7 +213,7 @@ private void Destroy(ApplicationEngine engine) engine.Snapshot.Storages.Delete(ckey); foreach (var (key, _) in engine.Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id))) engine.Snapshot.Storages.Delete(key); - engine.SendNotification(Hash, "Destory", new VM.Types.Array { hash.ToArray() }); + engine.SendNotification(Hash, "Destroy", new VM.Types.Array { hash.ToArray() }); } } } From 2c6d49bea97ef05a70dae4dab59910d69e9a94a0 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 5 Jan 2021 18:16:48 +0800 Subject: [PATCH 17/65] Store NefFile in ContractState (#2195) * Store NefFile in ContractState * deterministic Co-authored-by: Shargon --- src/neo/SmartContract/ContractState.cs | 24 ++++++++++-------- src/neo/SmartContract/MethodToken.cs | 25 ++++++++++++++----- .../Native/ContractManagement.cs | 10 +++----- .../SmartContract/Native/NativeContract.cs | 16 +++++++++--- src/neo/SmartContract/NefFile.cs | 15 +++++++++++ .../SmartContract/UT_ContractState.cs | 13 ++++++++-- .../SmartContract/UT_DeployedContract.cs | 4 +-- .../SmartContract/UT_InteropService.NEO.cs | 2 +- .../SmartContract/UT_InteropService.cs | 4 +-- .../SmartContract/UT_MethodToken.cs | 6 ++--- .../SmartContract/UT_SmartContractHelper.cs | 4 +-- .../SmartContract/UT_Syscalls.cs | 6 ++--- tests/neo.UnitTests/TestUtils.cs | 4 +-- 13 files changed, 90 insertions(+), 43 deletions(-) diff --git a/src/neo/SmartContract/ContractState.cs b/src/neo/SmartContract/ContractState.cs index f5dc3510b9..0d3495b035 100644 --- a/src/neo/SmartContract/ContractState.cs +++ b/src/neo/SmartContract/ContractState.cs @@ -3,7 +3,6 @@ using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; -using System; using System.Linq; using Array = Neo.VM.Types.Array; @@ -14,16 +13,18 @@ public class ContractState : IInteroperable public int Id; public ushort UpdateCounter; public UInt160 Hash; - public byte[] Script; + public NefFile Nef; public ContractManifest Manifest; + public byte[] Script => Nef.Script; + void IInteroperable.FromStackItem(StackItem stackItem) { Array array = (Array)stackItem; Id = (int)array[0].GetInteger(); UpdateCounter = (ushort)array[1].GetInteger(); Hash = new UInt160(array[2].GetSpan()); - Script = array[3].GetSpan().ToArray(); + Nef = array[3].GetSpan().AsSerializable(); Manifest = ContractManifest.Parse(array[4].GetSpan()); } @@ -40,18 +41,19 @@ public bool CanCall(ContractState targetContract, string targetMethod) public JObject ToJson() { - JObject json = new JObject(); - json["id"] = Id; - json["updatecounter"] = UpdateCounter; - json["hash"] = Hash.ToString(); - json["script"] = Convert.ToBase64String(Script); - json["manifest"] = Manifest.ToJson(); - return json; + return new JObject + { + ["id"] = Id, + ["updatecounter"] = UpdateCounter, + ["hash"] = Hash.ToString(), + ["nef"] = Nef.ToJson(), + ["manifest"] = Manifest.ToJson() + }; } public StackItem ToStackItem(ReferenceCounter referenceCounter) { - return new Array(referenceCounter, new StackItem[] { Id, (int)UpdateCounter, Hash.ToArray(), Script, Manifest.ToString() }); + return new Array(referenceCounter, new StackItem[] { Id, (int)UpdateCounter, Hash.ToArray(), Nef.ToArray(), Manifest.ToString() }); } } } diff --git a/src/neo/SmartContract/MethodToken.cs b/src/neo/SmartContract/MethodToken.cs index 10396757a7..42ba7d9c44 100644 --- a/src/neo/SmartContract/MethodToken.cs +++ b/src/neo/SmartContract/MethodToken.cs @@ -1,4 +1,5 @@ using Neo.IO; +using Neo.IO.Json; using System; using System.IO; @@ -9,25 +10,25 @@ public class MethodToken : ISerializable public UInt160 Hash; public string Method; public ushort ParametersCount; - public ushort RVCount; + public bool HasReturnValue; public CallFlags CallFlags; public int Size => UInt160.Length + // Hash Method.GetVarSize() + // Method sizeof(ushort) + // ParametersCount - sizeof(ushort) + // RVCount + sizeof(bool) + // HasReturnValue sizeof(CallFlags); // CallFlags void ISerializable.Deserialize(BinaryReader reader) { Hash = reader.ReadSerializable(); Method = reader.ReadVarString(32); + if (Method.StartsWith('_')) throw new FormatException(); ParametersCount = reader.ReadUInt16(); - RVCount = reader.ReadUInt16(); + HasReturnValue = reader.ReadBoolean(); CallFlags = (CallFlags)reader.ReadByte(); - if ((CallFlags & ~CallFlags.All) != 0) - throw new FormatException(); + if ((CallFlags & ~CallFlags.All) != 0) throw new FormatException(); } void ISerializable.Serialize(BinaryWriter writer) @@ -35,8 +36,20 @@ void ISerializable.Serialize(BinaryWriter writer) writer.Write(Hash); writer.WriteVarString(Method); writer.Write(ParametersCount); - writer.Write(RVCount); + writer.Write(HasReturnValue); writer.Write((byte)CallFlags); } + + public JObject ToJson() + { + return new JObject + { + ["hash"] = Hash.ToString(), + ["method"] = Method, + ["paramcount"] = ParametersCount, + ["hasreturnvalue"] = HasReturnValue, + ["callflags"] = CallFlags + }; + } } } diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 10ecf0d2ea..c297b1e95a 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -87,7 +87,7 @@ internal override void OnPersist(ApplicationEngine engine) engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState { Id = contract.Id, - Script = contract.Script, + Nef = contract.Nef, Hash = contract.Hash, Manifest = contract.Manifest })); @@ -145,7 +145,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma { Id = GetNextAvailableId(engine.Snapshot), UpdateCounter = 0, - Script = nef.Script, + Nef = nef, Hash = hash, Manifest = ContractManifest.Parse(manifest) }; @@ -180,10 +180,8 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest) if (nefFile.Length == 0) throw new ArgumentException($"Invalid NefFile Length: {nefFile.Length}"); - NefFile nef = nefFile.AsSerializable(); - - // Update script - contract.Script = nef.Script; + // Update nef + contract.Nef = nefFile.AsSerializable(); } if (manifest != null) { diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 375e0fb105..115a48a956 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -27,7 +27,8 @@ public abstract class NativeContract public static OracleContract Oracle { get; } = new OracleContract(); public string Name => GetType().Name; - public byte[] Script { get; } + public NefFile Nef { get; } + public byte[] Script => Nef.Script; public UInt160 Hash { get; } public abstract int Id { get; } public ContractManifest Manifest { get; } @@ -35,13 +36,22 @@ public abstract class NativeContract protected NativeContract() { + byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(Name); sb.EmitSysCall(ApplicationEngine.System_Contract_CallNative); - this.Script = sb.ToArray(); + script = sb.ToArray(); } - this.Hash = Helper.GetContractHash(UInt160.Zero, Script); + this.Nef = new NefFile + { + Compiler = nameof(ScriptBuilder), + Version = "3.0", + Tokens = System.Array.Empty(), + Script = script + }; + this.Nef.CheckSum = NefFile.ComputeChecksum(Nef); + this.Hash = Helper.GetContractHash(UInt160.Zero, script); List descriptors = new List(); foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { diff --git a/src/neo/SmartContract/NefFile.cs b/src/neo/SmartContract/NefFile.cs index 07c9801cbd..7eac98a35d 100644 --- a/src/neo/SmartContract/NefFile.cs +++ b/src/neo/SmartContract/NefFile.cs @@ -1,7 +1,9 @@ using Neo.Cryptography; using Neo.IO; +using Neo.IO.Json; using System; using System.IO; +using System.Linq; namespace Neo.SmartContract { @@ -117,5 +119,18 @@ public static uint ComputeChecksum(NefFile file) { return BitConverter.ToUInt32(Crypto.Hash256(file.ToArray().AsSpan(..^sizeof(int)))); } + + public JObject ToJson() + { + return new JObject + { + ["magic"] = Magic, + ["compiler"] = Compiler, + ["version"] = Version, + ["tokens"] = new JArray(Tokens.Select(p => p.ToJson())), + ["script"] = Convert.ToBase64String(Script), + ["checksum"] = CheckSum + }; + } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractState.cs b/tests/neo.UnitTests/SmartContract/UT_ContractState.cs index a476ec903c..77431c08fa 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractState.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractState.cs @@ -3,6 +3,8 @@ using Neo.IO.Json; using Neo.SmartContract; using Neo.SmartContract.Manifest; +using Neo.VM; +using System; namespace Neo.UnitTests.SmartContract { @@ -19,10 +21,17 @@ public void TestSetup() manifest = TestUtils.CreateDefaultManifest(); contract = new ContractState { - Script = script, + Nef = new NefFile + { + Compiler = nameof(ScriptBuilder), + Version = typeof(ScriptBuilder).Assembly.GetVersion(), + Tokens = Array.Empty(), + Script = script + }, Hash = script.ToScriptHash(), Manifest = manifest }; + contract.Nef.CheckSum = NefFile.ComputeChecksum(contract.Nef); } [TestMethod] @@ -56,7 +65,7 @@ public void TestToJson() { JObject json = contract.ToJson(); json["hash"].AsString().Should().Be("0x820944cfdc70976602d71b0091445eedbc661bc5"); - json["script"].AsString().Should().Be("AQ=="); + json["nef"]["script"].AsString().Should().Be("AQ=="); json["manifest"].AsString().Should().Be(manifest.ToJson().AsString()); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs b/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs index 0bb2a86419..96ae48d17b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs @@ -26,7 +26,7 @@ public void TestGetAddress() } } }, - Script = new byte[] { 1, 2, 3 }, + Nef = new NefFile { Script = new byte[] { 1, 2, 3 } }, Hash = new byte[] { 1, 2, 3 }.ToScriptHash() }); @@ -54,7 +54,7 @@ public void TestErrors() } } }, - Script = new byte[] { 1, 2, 3 } + Nef = new NefFile { Script = new byte[] { 1, 2, 3 } } })); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 98cfcc1f14..a45b2bed46 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -121,7 +121,7 @@ public void TestAccount_IsStandard() engine.LoadScript(new byte[] { 0x01 }); engine.IsStandardContract(state.Hash).Should().BeFalse(); - state.Script = Contract.CreateSignatureRedeemScript(Blockchain.StandbyValidators[0]); + state.Nef.Script = Contract.CreateSignatureRedeemScript(Blockchain.StandbyValidators[0]); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); engine.IsStandardContract(state.Hash).Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index ba67c15287..60ff884f44 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -52,7 +52,7 @@ public void Runtime_GetNotifications_Test() snapshot.DeleteContract(scriptHash2); snapshot.AddContract(scriptHash2, new ContractState() { - Script = script.ToArray(), + Nef = new NefFile { Script = script.ToArray() }, Hash = script.ToArray().ToScriptHash(), Manifest = TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer), }); @@ -217,7 +217,7 @@ public void TestExecutionEngine_GetCallingScriptHash() var contract = new ContractState() { Manifest = TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer), - Script = scriptA.ToArray(), + Nef = new NefFile { Script = scriptA.ToArray() }, Hash = scriptA.ToArray().ToScriptHash() }; engine = GetEngine(true, true, false); diff --git a/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs b/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs index e200da4f62..8d530eefd9 100644 --- a/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs +++ b/tests/neo.UnitTests/SmartContract/UT_MethodToken.cs @@ -17,7 +17,7 @@ public void TestSerialize() Hash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), Method = "myMethod", ParametersCount = 123, - RVCount = 456 + HasReturnValue = true }; var copy = result.ToArray().AsSerializable(); @@ -26,7 +26,7 @@ public void TestSerialize() Assert.AreEqual("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", copy.Hash.ToString()); Assert.AreEqual("myMethod", copy.Method); Assert.AreEqual(123, copy.ParametersCount); - Assert.AreEqual(456, copy.RVCount); + Assert.AreEqual(true, copy.HasReturnValue); } [TestMethod] @@ -38,7 +38,7 @@ public void TestSerializeErrors() Hash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), Method = "myLongMethod", ParametersCount = 123, - RVCount = 456 + HasReturnValue = true }; Assert.ThrowsException(() => result.ToArray().AsSerializable()); diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 1dbd8e2f4a..a23513f2a2 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -149,7 +149,7 @@ public void TestVerifyWitnesses() }; snapshot3.AddContract(UInt160.Zero, new ContractState() { - Script = Array.Empty(), + Nef = new NefFile { Script = Array.Empty() }, Hash = Array.Empty().ToScriptHash(), Manifest = TestUtils.CreateManifest("verify", ContractParameterType.Boolean, ContractParameterType.Signature), }); @@ -159,7 +159,7 @@ public void TestVerifyWitnesses() var contract = new ContractState() { - Script = "11".HexToBytes(), // 17 PUSH1 + Nef = new NefFile { Script = "11".HexToBytes() }, // 17 PUSH1 Hash = "11".HexToBytes().ToScriptHash(), Manifest = TestUtils.CreateManifest("verify", ContractParameterType.Boolean, ContractParameterType.Signature), // Offset = 0 }; diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 3a06e062a0..d39bcf9a8b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -324,9 +324,9 @@ public void System_Runtime_GetInvocationCounter() { script.EmitSysCall(ApplicationEngine.System_Runtime_GetInvocationCounter); - contractA = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray() }; - contractB = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; - contractC = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; + contractA = new ContractState() { Nef = new NefFile { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray() } }; + contractB = new ContractState() { Nef = new NefFile { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() } }; + contractC = new ContractState() { Nef = new NefFile { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() } }; contractA.Hash = contractA.Script.ToScriptHash(); contractB.Hash = contractB.Script.ToScriptHash(); contractC.Hash = contractC.Script.ToScriptHash(); diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index aa5d8623ff..e85b2e653c 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -111,7 +111,7 @@ internal static ContractState GetContract(string method = "test", int parameters return new ContractState { Id = 0x43000000, - Script = new byte[] { 0x01, 0x01, 0x01, 0x01 }, + Nef = new NefFile { Script = new byte[] { 0x01, 0x01, 0x01, 0x01 } }, Hash = new byte[] { 0x01, 0x01, 0x01, 0x01 }.ToScriptHash(), Manifest = CreateManifest(method, ContractParameterType.Any, Enumerable.Repeat(ContractParameterType.Any, parametersCount).ToArray()) }; @@ -122,7 +122,7 @@ internal static ContractState GetContract(byte[] script) return new ContractState { Id = 1, - Script = script, + Nef = new NefFile { Script = script }, Manifest = CreateDefaultManifest() }; } From 36faae711c67642f52e32e54bb4c090282b5a9c9 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 5 Jan 2021 19:08:00 +0800 Subject: [PATCH 18/65] Add GetKey() in NonfungibleToken (#2199) --- src/neo/SmartContract/Native/NonfungibleToken.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index 45969da8b5..4ad9799269 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -62,9 +62,11 @@ protected NonfungibleToken() Manifest.Abi.Events = events.ToArray(); } + protected virtual byte[] GetKey(byte[] tokenId) => tokenId; + protected void Mint(ApplicationEngine engine, TokenState token) { - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Token).Add(token.Id), new StorageItem(token)); + engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Token).Add(GetKey(token.Id)), new StorageItem(token)); NFTAccountState account = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Account).Add(token.Owner), () => new StorageItem(new NFTAccountState())).GetInteroperable(); account.Add(token.Id); engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero)).Add(1); @@ -73,7 +75,7 @@ protected void Mint(ApplicationEngine engine, TokenState token) protected void Burn(ApplicationEngine engine, byte[] tokenId) { - StorageKey key_token = CreateStorageKey(Prefix_Token).Add(tokenId); + StorageKey key_token = CreateStorageKey(Prefix_Token).Add(GetKey(tokenId)); TokenState token = engine.Snapshot.Storages.TryGet(key_token)?.GetInteroperable(); if (token is null) throw new InvalidOperationException(); engine.Snapshot.Storages.Delete(key_token); @@ -96,13 +98,13 @@ public BigInteger TotalSupply(StoreView snapshot) [ContractMethod(0_01000000, CallFlags.ReadStates)] public UInt160 OwnerOf(StoreView snapshot, byte[] tokenId) { - return snapshot.Storages[CreateStorageKey(Prefix_Token).Add(tokenId)].GetInteroperable().Owner; + return snapshot.Storages[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().Owner; } [ContractMethod(0_01000000, CallFlags.ReadStates)] public JObject Properties(StoreView snapshot, byte[] tokenId) { - return snapshot.Storages[CreateStorageKey(Prefix_Token).Add(tokenId)].GetInteroperable().ToJson(); + return snapshot.Storages[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().ToJson(); } [ContractMethod(0_01000000, CallFlags.ReadStates)] @@ -132,7 +134,7 @@ public IIterator TokensOf(StoreView snapshot, UInt160 owner) protected bool Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) { if (to is null) throw new ArgumentNullException(nameof(to)); - TokenState token = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Token).Add(tokenId))?.GetInteroperable(); + TokenState token = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Token).Add(GetKey(tokenId)))?.GetInteroperable(); if (token is null) throw new ArgumentException(); UInt160 from = token.Owner; if (!from.Equals(engine.CallingScriptHash) && !engine.CheckWitnessInternal(from)) From 83857d232e755cbc3848a9158e96b54cdfcedb09 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 6 Jan 2021 10:15:37 +0100 Subject: [PATCH 19/65] Allow plugins to send custom messages (#2101) --- src/neo/Ledger/Blockchain.cs | 34 ++++++- src/neo/Network/P2P/Message.cs | 1 + src/neo/Network/P2P/MessageCommand.cs | 2 + .../Network/P2P/Payloads/ExtensiblePayload.cs | 99 +++++++++++++++++++ src/neo/Network/P2P/Payloads/InventoryType.cs | 1 + .../Network/P2P/RemoteNode.ProtocolHandler.cs | 5 +- src/neo/Network/P2P/RemoteNode.cs | 2 + src/neo/Network/P2P/TaskManager.cs | 2 +- src/neo/Plugins/IP2PPlugin.cs | 1 + .../P2P/Payloads/UT_ExtensiblePayload.cs | 47 +++++++++ tests/neo.UnitTests/Plugins/UT_Plugin.cs | 6 +- 11 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 src/neo/Network/P2P/Payloads/ExtensiblePayload.cs create mode 100644 tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index a1be404406..6635167368 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -10,10 +10,12 @@ using Neo.Persistence; using Neo.Plugins; using Neo.SmartContract; +using Neo.SmartContract.Native; using Neo.VM; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -66,6 +68,7 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu private readonly Dictionary block_cache_unverified = new Dictionary(); internal readonly RelayCache RelayCache = new RelayCache(100); private SnapshotView currentSnapshot; + private ImmutableHashSet extensibleWitnessWhiteList; public IStore Store { get; } public ReadOnlyView View { get; } @@ -288,8 +291,12 @@ private void OnInventory(IInventory inventory, bool relay = true) Transaction transaction => OnNewTransaction(transaction), _ => OnNewInventory(inventory) }; - if (relay && result == VerifyResult.Succeed) - system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = inventory }); + if (result == VerifyResult.Succeed) + { + if (relay) system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = inventory }); + foreach (IP2PPlugin plugin in Plugin.P2PPlugins) + plugin.OnVerifiedInventory(inventory); + } SendRelayResult(inventory, result); } @@ -517,6 +524,29 @@ private void SendRelayResult(IInventory inventory, VerifyResult result) private void UpdateCurrentSnapshot() { Interlocked.Exchange(ref currentSnapshot, GetSnapshot())?.Dispose(); + var builder = ImmutableHashSet.CreateBuilder(); + builder.Add(NativeContract.NEO.GetCommitteeAddress(currentSnapshot)); + var validators = NativeContract.NEO.GetNextBlockValidators(currentSnapshot); + builder.Add(GetConsensusAddress(validators)); + builder.UnionWith(validators.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); + var oracles = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.Oracle, currentSnapshot.Height); + if (oracles.Length > 0) + { + builder.Add(GetConsensusAddress(oracles)); + builder.UnionWith(oracles.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); + } + var stateValidators = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.StateValidator, currentSnapshot.Height); + if (stateValidators.Length > 0) + { + builder.Add(GetConsensusAddress(stateValidators)); + builder.UnionWith(stateValidators.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); + } + extensibleWitnessWhiteList = builder.ToImmutable(); + } + + internal bool IsExtensibleWitnessWhiteListed(UInt160 address) + { + return extensibleWitnessWhiteList.Contains(address); } } diff --git a/src/neo/Network/P2P/Message.cs b/src/neo/Network/P2P/Message.cs index 397ee4d033..ed0d23f883 100644 --- a/src/neo/Network/P2P/Message.cs +++ b/src/neo/Network/P2P/Message.cs @@ -39,6 +39,7 @@ public static Message Create(MessageCommand command, ISerializable payload = nul bool tryCompression = command == MessageCommand.Block || command == MessageCommand.Consensus || + command == MessageCommand.Extensible || command == MessageCommand.Transaction || command == MessageCommand.Headers || command == MessageCommand.Addr || diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index 89c21597cf..7438d90850 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -40,6 +40,8 @@ public enum MessageCommand : byte Block = 0x2c, [ReflectionCache(typeof(ConsensusPayload))] Consensus = 0x2d, + [ReflectionCache(typeof(ExtensiblePayload))] + Extensible = 0x2e, Reject = 0x2f, //SPV protocol diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs new file mode 100644 index 0000000000..d7a0902e42 --- /dev/null +++ b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -0,0 +1,99 @@ +using Neo.IO; +using Neo.Ledger; +using Neo.Persistence; +using Neo.SmartContract; +using System; +using System.IO; + +namespace Neo.Network.P2P.Payloads +{ + public class ExtensiblePayload : IInventory + { + public string Category; + public uint ValidBlockStart; + public uint ValidBlockEnd; + public UInt160 Sender; + public byte[] Data; + public Witness Witness; + + private UInt256 _hash = null; + public UInt256 Hash + { + get + { + if (_hash == null) + { + _hash = this.CalculateHash(); + } + return _hash; + } + } + + InventoryType IInventory.InventoryType => InventoryType.Extensible; + + public int Size => + Category.GetVarSize() + //Category + sizeof(uint) + //ValidBlockStart + sizeof(uint) + //ValidBlockEnd + UInt160.Length + //Sender + Data.GetVarSize() + //Data + 1 + Witness.Size; //Witness + + Witness[] IVerifiable.Witnesses + { + get + { + return new[] { Witness }; + } + set + { + if (value.Length != 1) throw new ArgumentException(); + Witness = value[0]; + } + } + + void ISerializable.Deserialize(BinaryReader reader) + { + ((IVerifiable)this).DeserializeUnsigned(reader); + if (reader.ReadByte() != 1) throw new FormatException(); + Witness = reader.ReadSerializable(); + } + + void IVerifiable.DeserializeUnsigned(BinaryReader reader) + { + Category = reader.ReadVarString(32); + ValidBlockStart = reader.ReadUInt32(); + ValidBlockEnd = reader.ReadUInt32(); + if (ValidBlockStart > ValidBlockEnd) throw new FormatException(); + Sender = reader.ReadSerializable(); + Data = reader.ReadVarBytes(ushort.MaxValue); + } + + UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot) + { + return new[] { Sender }; // This address should be checked by consumer + } + + void ISerializable.Serialize(BinaryWriter writer) + { + ((IVerifiable)this).SerializeUnsigned(writer); + writer.Write((byte)1); writer.Write(Witness); + } + + void IVerifiable.SerializeUnsigned(BinaryWriter writer) + { + writer.WriteVarString(Category); + writer.Write(ValidBlockStart); + writer.Write(ValidBlockEnd); + writer.Write(Sender); + writer.WriteVarBytes(Data); + } + + public bool Verify(StoreView snapshot) + { + if (snapshot.PersistingBlock.Index < ValidBlockStart || snapshot.PersistingBlock.Index > ValidBlockEnd) return false; + if (!Blockchain.Singleton.IsExtensibleWitnessWhiteListed(Sender)) return false; + return this.VerifyWitnesses(snapshot, 0_02000000); + } + } +} diff --git a/src/neo/Network/P2P/Payloads/InventoryType.cs b/src/neo/Network/P2P/Payloads/InventoryType.cs index 0a1b831d12..33b9f2fc25 100644 --- a/src/neo/Network/P2P/Payloads/InventoryType.cs +++ b/src/neo/Network/P2P/Payloads/InventoryType.cs @@ -4,6 +4,7 @@ public enum InventoryType : byte { TX = MessageCommand.Transaction, Block = MessageCommand.Block, + Extensible = MessageCommand.Extensible, Consensus = MessageCommand.Consensus } } diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 8cda813e72..48e1cb3143 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -62,10 +62,9 @@ private void OnMessage(Message msg) OnAddrMessageReceived((AddrPayload)msg.Payload); break; case MessageCommand.Block: - OnInventoryReceived((Block)msg.Payload); - break; case MessageCommand.Consensus: - OnInventoryReceived((ConsensusPayload)msg.Payload); + case MessageCommand.Extensible: + OnInventoryReceived((IInventory)msg.Payload); break; case MessageCommand.FilterAdd: OnFilterAddMessageReceived((FilterAddPayload)msg.Payload); diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index 9440584ddc..8e1343ec0b 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -87,6 +87,7 @@ private void EnqueueMessage(Message message) { case MessageCommand.Alert: case MessageCommand.Consensus: + case MessageCommand.Extensible: case MessageCommand.FilterAdd: case MessageCommand.FilterClear: case MessageCommand.FilterLoad: @@ -223,6 +224,7 @@ internal protected override bool IsHighPriority(object message) switch (msg.Command) { case MessageCommand.Consensus: + case MessageCommand.Extensible: case MessageCommand.FilterAdd: case MessageCommand.FilterClear: case MessageCommand.FilterLoad: diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index fb1ade45bd..688fa13cff 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -342,7 +342,7 @@ internal protected override bool IsHighPriority(object message) case TaskManager.RestartTasks _: return true; case TaskManager.NewTasks tasks: - if (tasks.Payload.Type == InventoryType.Block || tasks.Payload.Type == InventoryType.Consensus) + if (tasks.Payload.Type == InventoryType.Block || tasks.Payload.Type == InventoryType.Consensus || tasks.Payload.Type == InventoryType.Extensible) return true; return false; default: diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs index 40c083545b..d6ba709e36 100644 --- a/src/neo/Plugins/IP2PPlugin.cs +++ b/src/neo/Plugins/IP2PPlugin.cs @@ -7,5 +7,6 @@ public interface IP2PPlugin { bool OnP2PMessage(Message message) => true; bool OnConsensusMessage(ConsensusPayload payload) => true; + void OnVerifiedInventory(IInventory inventory); } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs new file mode 100644 index 0000000000..0fd01642b1 --- /dev/null +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs @@ -0,0 +1,47 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using System; + +namespace Neo.UnitTests.Network.P2P.Payloads +{ + [TestClass] + public class UT_ExtensiblePayload + { + [TestMethod] + public void Size_Get() + { + var test = new ExtensiblePayload() + { + Sender = Array.Empty().ToScriptHash(), + Category = "123", + Data = new byte[] { 1, 2, 3 }, + Witness = new Witness() { InvocationScript = new byte[] { 3, 5, 6 }, VerificationScript = Array.Empty() } + }; + test.Size.Should().Be(42); + } + + [TestMethod] + public void DeserializeAndSerialize() + { + var test = new ExtensiblePayload() + { + Category = "123", + ValidBlockStart = 456, + ValidBlockEnd = 789, + Sender = Array.Empty().ToScriptHash(), + Data = new byte[] { 1, 2, 3 }, + Witness = new Witness() { InvocationScript = new byte[] { 3, 5, 6 }, VerificationScript = Array.Empty() } + }; + var clone = test.ToArray().AsSerializable(); + + Assert.AreEqual(test.Sender, clone.Witness.ScriptHash); + Assert.AreEqual(test.Hash, clone.Hash); + Assert.AreEqual(test.ValidBlockStart, clone.ValidBlockStart); + Assert.AreEqual(test.ValidBlockEnd, clone.ValidBlockEnd); + Assert.AreEqual(test.Category, clone.Category); + } + } +} diff --git a/tests/neo.UnitTests/Plugins/UT_Plugin.cs b/tests/neo.UnitTests/Plugins/UT_Plugin.cs index d56dcd8c15..de32a45898 100644 --- a/tests/neo.UnitTests/Plugins/UT_Plugin.cs +++ b/tests/neo.UnitTests/Plugins/UT_Plugin.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P.Payloads; using Neo.Plugins; using System; @@ -10,7 +11,10 @@ public class UT_Plugin { private static readonly object locker = new object(); - private class DummyP2PPlugin : IP2PPlugin { } + private class DummyP2PPlugin : IP2PPlugin + { + public void OnVerifiedInventory(IInventory inventory) { } + } private class dummyPersistencePlugin : IPersistencePlugin { } [TestMethod] From c0604551b2475be84bb74054a38257dc09a0148e Mon Sep 17 00:00:00 2001 From: Mirella de Medeiros Date: Fri, 8 Jan 2021 11:31:37 -0300 Subject: [PATCH 20/65] Include EmitPush to ContractParameterType.Map in Helper (#2206) --- src/neo/VM/Helper.cs | 11 ++++++++++- tests/neo.UnitTests/VM/UT_Helper.cs | 8 ++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 257de647e4..764f7fd9ab 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -25,7 +25,7 @@ public static ScriptBuilder CreateArray(this ScriptBuilder sb, IReadOnlyList< return sb.Emit(OpCode.PACK); } - public static ScriptBuilder CreateMap(this ScriptBuilder sb, IReadOnlyDictionary map = null) + public static ScriptBuilder CreateMap(this ScriptBuilder sb, IEnumerable> map = null) { sb.Emit(OpCode.NEWMAP); if (map != null) @@ -129,6 +129,12 @@ public static ScriptBuilder EmitPush(this ScriptBuilder sb, ContractParameter pa sb.Emit(OpCode.PACK); } break; + case ContractParameterType.Map: + { + var pairs = (IList>)parameter.Value; + sb.CreateMap(pairs); + } + break; default: throw new ArgumentException(); } @@ -181,6 +187,9 @@ public static ScriptBuilder EmitPush(this ScriptBuilder sb, object obj) case Enum data: sb.EmitPush(BigInteger.Parse(data.ToString("d"))); break; + case ContractParameter data: + sb.EmitPush(data); + break; case null: sb.Emit(OpCode.PUSHNULL); break; diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index cd84fdc773..3b8333e531 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -250,10 +250,14 @@ public void TestEmitPush2() TestEmitPush2PublicKey(); TestEmitPush2String(); TestEmitPush2Array(); + TestEmitPush2Map(); + } + private void TestEmitPush2Map() + { ScriptBuilder sb = new ScriptBuilder(); - Action action = () => sb.EmitPush(new ContractParameter(ContractParameterType.Map)); - action.Should().Throw(); + sb.EmitPush(new ContractParameter(ContractParameterType.Map)); + CollectionAssert.AreEqual(new[] { (byte)OpCode.NEWMAP }, sb.ToArray()); } private void TestEmitPush2Array() From 581a681ba4c43e723f055b7d4dfe6df821274965 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 10 Jan 2021 09:49:48 +0800 Subject: [PATCH 21/65] Remove PersistingBlock from snapshot (#2209) --- src/neo/Ledger/Blockchain.cs | 7 +- .../Network/P2P/Payloads/ExtensiblePayload.cs | 2 +- src/neo/Persistence/ClonedView.cs | 1 - src/neo/Persistence/StoreView.cs | 1 - src/neo/Plugins/IApplicationEngineProvider.cs | 2 +- .../ApplicationEngine.Contract.cs | 4 +- .../ApplicationEngine.Runtime.cs | 2 +- src/neo/SmartContract/ApplicationEngine.cs | 13 +- src/neo/SmartContract/Helper.cs | 2 +- .../Native/ContractManagement.cs | 2 +- src/neo/SmartContract/Native/GasToken.cs | 4 +- src/neo/SmartContract/Native/NeoToken.cs | 18 +- .../SmartContract/Native/OracleContract.cs | 4 +- .../SmartContract/Native/RoleManagement.cs | 4 +- .../Extensions/NativeContractExtensions.cs | 8 +- .../Nep17NativeContractExtensions.cs | 4 +- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 4 +- .../UT_TransactionVerificationContext.cs | 4 +- .../Network/P2P/Payloads/UT_Transaction.cs | 14 +- .../SmartContract/Native/UT_GasToken.cs | 31 ++- .../SmartContract/Native/UT_NativeContract.cs | 8 +- .../SmartContract/Native/UT_NeoToken.cs | 184 +++++++++--------- .../SmartContract/Native/UT_PolicyContract.cs | 71 ++++--- .../SmartContract/Native/UT_RoleManagement.cs | 6 +- .../SmartContract/UT_ApplicationEngine.cs | 8 +- .../UT_ApplicationEngineProvider.cs | 12 +- .../SmartContract/UT_InteropPrices.cs | 6 +- .../SmartContract/UT_InteropService.NEO.cs | 4 - .../SmartContract/UT_InteropService.cs | 38 +--- .../SmartContract/UT_Syscalls.cs | 2 +- 30 files changed, 218 insertions(+), 252 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 6635167368..01898679f7 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -397,8 +397,7 @@ private void Persist(Block block) snapshot.HeaderHashIndex.GetAndChange().Set(block); } List all_application_executed = new List(); - snapshot.PersistingBlock = block; - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, block)) { engine.LoadScript(onPersistScript); if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); @@ -420,7 +419,7 @@ private void Persist(Block block) clonedSnapshot.Transactions.Add(tx.Hash, state); clonedSnapshot.Transactions.Commit(); - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, tx.SystemFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, block, tx.SystemFee)) { engine.LoadScript(tx.Script); state.VMState = engine.Execute(); @@ -438,7 +437,7 @@ private void Persist(Block block) } } snapshot.BlockHashIndex.GetAndChange().Set(block); - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, block)) { engine.LoadScript(postPersistScript); if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs index d7a0902e42..6b21a81276 100644 --- a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -91,7 +91,7 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) public bool Verify(StoreView snapshot) { - if (snapshot.PersistingBlock.Index < ValidBlockStart || snapshot.PersistingBlock.Index > ValidBlockEnd) return false; + if (snapshot.Height < ValidBlockStart || snapshot.Height >= ValidBlockEnd) return false; if (!Blockchain.Singleton.IsExtensibleWitnessWhiteListed(Sender)) return false; return this.VerifyWitnesses(snapshot, 0_02000000); } diff --git a/src/neo/Persistence/ClonedView.cs b/src/neo/Persistence/ClonedView.cs index 03d945c849..3b9b8017e6 100644 --- a/src/neo/Persistence/ClonedView.cs +++ b/src/neo/Persistence/ClonedView.cs @@ -15,7 +15,6 @@ internal class ClonedView : StoreView public ClonedView(StoreView view) { - this.PersistingBlock = view.PersistingBlock; this.Blocks = view.Blocks.CreateSnapshot(); this.Transactions = view.Transactions.CreateSnapshot(); this.Storages = view.Storages.CreateSnapshot(); diff --git a/src/neo/Persistence/StoreView.cs b/src/neo/Persistence/StoreView.cs index 4671c12381..a31d27ec57 100644 --- a/src/neo/Persistence/StoreView.cs +++ b/src/neo/Persistence/StoreView.cs @@ -10,7 +10,6 @@ namespace Neo.Persistence /// public abstract class StoreView { - public Block PersistingBlock { get; internal set; } public abstract DataCache Blocks { get; } public abstract DataCache Transactions { get; } public abstract DataCache Storages { get; } diff --git a/src/neo/Plugins/IApplicationEngineProvider.cs b/src/neo/Plugins/IApplicationEngineProvider.cs index ce42f39749..df3427219c 100644 --- a/src/neo/Plugins/IApplicationEngineProvider.cs +++ b/src/neo/Plugins/IApplicationEngineProvider.cs @@ -6,6 +6,6 @@ namespace Neo.Plugins { public interface IApplicationEngineProvider { - ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas); + ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock, long gas); } } diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 7e9f9e9c1b..cd0df5423d 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -132,7 +132,7 @@ protected internal void NativeOnPersist() if (Trigger != TriggerType.OnPersist) throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) - if (contract.ActiveBlockIndex <= Snapshot.PersistingBlock.Index) + if (contract.ActiveBlockIndex <= PersistingBlock.Index) contract.OnPersist(this); } @@ -141,7 +141,7 @@ protected internal void NativePostPersist() if (Trigger != TriggerType.PostPersist) throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) - if (contract.ActiveBlockIndex <= Snapshot.PersistingBlock.Index) + if (contract.ActiveBlockIndex <= PersistingBlock.Index) contract.PostPersist(this); } } diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index 601af69037..8d74182e68 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -36,7 +36,7 @@ protected internal string GetPlatform() protected internal ulong GetTime() { - return Snapshot.PersistingBlock.Timestamp; + return PersistingBlock.Timestamp; } protected internal IInteroperable GetScriptContainer() diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index d04c7e2b9b..3fa14e5683 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -43,6 +43,7 @@ public partial class ApplicationEngine : ExecutionEngine public TriggerType Trigger { get; } public IVerifiable ScriptContainer { get; } public StoreView Snapshot { get; } + public Block PersistingBlock { get; } public long GasConsumed { get; private set; } = 0; public long GasLeft => gas_amount - GasConsumed; public Exception FaultException { get; private set; } @@ -51,11 +52,12 @@ public partial class ApplicationEngine : ExecutionEngine public UInt160 EntryScriptHash => EntryContext?.GetScriptHash(); public IReadOnlyList Notifications => notifications ?? (IReadOnlyList)Array.Empty(); - protected ApplicationEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas) + protected ApplicationEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock, long gas) { this.Trigger = trigger; this.ScriptContainer = container; this.Snapshot = snapshot; + this.PersistingBlock = persistingBlock; this.gas_amount = gas; this.exec_fee_factor = snapshot is null ? PolicyContract.DefaultExecFeeFactor : NativeContract.Policy.GetExecFeeFactor(Snapshot); this.StoragePrice = snapshot is null ? PolicyContract.DefaultStoragePrice : NativeContract.Policy.GetStoragePrice(Snapshot); @@ -91,10 +93,10 @@ internal T CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, st return (T)Convert(Pop(), new InteropParameterDescriptor(typeof(T))); } - public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas = TestModeGas) + public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock = null, long gas = TestModeGas) { - return applicationEngineProvider?.Create(trigger, container, snapshot, gas) - ?? new ApplicationEngine(trigger, container, snapshot, gas); + return applicationEngineProvider?.Create(trigger, container, snapshot, persistingBlock, gas) + ?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, gas); } protected override void LoadContext(ExecutionContext context) @@ -296,8 +298,7 @@ public static ApplicationEngine Run(byte[] script, StoreView snapshot = null, IV disposable = Blockchain.Singleton.GetSnapshot(); snapshot = disposable; } - snapshot.PersistingBlock = persistingBlock ?? snapshot.PersistingBlock ?? CreateDummyBlock(snapshot); - ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, gas); + ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock ?? CreateDummyBlock(snapshot), gas); if (disposable != null) engine.Disposables.Add(disposable); engine.LoadScript(script, initialPosition: offset); engine.Execute(); diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 4c1a6a55a6..b4d22851da 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -178,7 +178,7 @@ public static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snapsh internal static bool VerifyWitness(this IVerifiable verifiable, StoreView snapshot, UInt160 hash, Witness witness, long gas, out long fee) { fee = 0; - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, verifiable, snapshot?.Clone(), gas)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, verifiable, snapshot?.Clone(), null, gas)) { CallFlags callFlags = !witness.VerificationScript.IsStandardContract() ? CallFlags.ReadStates : CallFlags.None; byte[] verification = witness.VerificationScript; diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index c297b1e95a..e161fa3ba3 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -82,7 +82,7 @@ internal override void OnPersist(ApplicationEngine engine) { foreach (NativeContract contract in Contracts) { - if (contract.ActiveBlockIndex != engine.Snapshot.PersistingBlock.Index) + if (contract.ActiveBlockIndex != engine.PersistingBlock.Index) continue; engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState { diff --git a/src/neo/SmartContract/Native/GasToken.cs b/src/neo/SmartContract/Native/GasToken.cs index 0a9c85de4d..db3e990c57 100644 --- a/src/neo/SmartContract/Native/GasToken.cs +++ b/src/neo/SmartContract/Native/GasToken.cs @@ -23,13 +23,13 @@ internal override void Initialize(ApplicationEngine engine) internal override void OnPersist(ApplicationEngine engine) { long totalNetworkFee = 0; - foreach (Transaction tx in engine.Snapshot.PersistingBlock.Transactions) + foreach (Transaction tx in engine.PersistingBlock.Transactions) { Burn(engine, tx.Sender, tx.SystemFee + tx.NetworkFee); totalNetworkFee += tx.NetworkFee; } ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot); - UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.Snapshot.PersistingBlock.ConsensusData.PrimaryIndex]).ToScriptHash(); + UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.PersistingBlock.ConsensusData.PrimaryIndex]).ToScriptHash(); Mint(engine, primary, totalNetworkFee, false); } } diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index 85a81afac6..e9c80ed43d 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -59,10 +59,10 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco private void DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state) { // PersistingBlock is null when running under the debugger - if (engine.Snapshot.PersistingBlock == null) return; + if (engine.PersistingBlock is null) return; - BigInteger gas = CalculateBonus(engine.Snapshot, state.VoteTo, state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index); - state.BalanceHeight = engine.Snapshot.PersistingBlock.Index; + BigInteger gas = CalculateBonus(engine.Snapshot, state.VoteTo, state.Balance, state.BalanceHeight, engine.PersistingBlock.Index); + state.BalanceHeight = engine.PersistingBlock.Index; GAS.Mint(engine, account, gas, true); } @@ -132,7 +132,7 @@ internal override void Initialize(ApplicationEngine engine) internal override void OnPersist(ApplicationEngine engine) { // Set next committee - if (ShouldRefreshCommittee(engine.Snapshot.PersistingBlock.Index)) + if (ShouldRefreshCommittee(engine.PersistingBlock.Index)) { StorageItem storageItem = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Committee)); var cachedCommittee = storageItem.GetInteroperable(); @@ -147,7 +147,7 @@ internal override void PostPersist(ApplicationEngine engine) int m = ProtocolSettings.Default.CommitteeMembersCount; int n = ProtocolSettings.Default.ValidatorsCount; - int index = (int)(engine.Snapshot.PersistingBlock.Index % (uint)m); + int index = (int)(engine.PersistingBlock.Index % (uint)m); var gasPerBlock = GetGasPerBlock(engine.Snapshot); var committee = GetCommitteeFromCache(engine.Snapshot); var pubkey = committee.ElementAt(index).PublicKey; @@ -156,7 +156,7 @@ internal override void PostPersist(ApplicationEngine engine) // Record the cumulative reward of the voters of committee - if (ShouldRefreshCommittee(engine.Snapshot.PersistingBlock.Index)) + if (ShouldRefreshCommittee(engine.PersistingBlock.Index)) { BigInteger voterRewardOfEachCommittee = gasPerBlock * VoterRewardRatio * 100000000L * m / (m + n) / 100; // Zoom in 100000000 times, and the final calculation should be divided 100000000L for (index = 0; index < committee.Count; index++) @@ -166,7 +166,7 @@ internal override void PostPersist(ApplicationEngine engine) if (member.Votes > 0) { BigInteger voterSumRewardPerNEO = factor * voterRewardOfEachCommittee / member.Votes; - StorageKey voterRewardKey = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(member.PublicKey).AddBigEndian(engine.Snapshot.PersistingBlock.Index + 1); + StorageKey voterRewardKey = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(member.PublicKey).AddBigEndian(engine.PersistingBlock.Index + 1); byte[] border = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(member.PublicKey).ToArray(); (_, var item) = engine.Snapshot.Storages.FindRange(voterRewardKey.ToArray(), border, SeekDirection.Backward).FirstOrDefault(); voterSumRewardPerNEO += (item ?? BigInteger.Zero); @@ -183,7 +183,7 @@ private bool SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) throw new ArgumentOutOfRangeException(nameof(gasPerBlock)); if (!CheckCommittee(engine)) return false; - uint index = engine.Snapshot.PersistingBlock.Index + 1; + uint index = engine.PersistingBlock.Index + 1; StorageItem entry = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(index), () => new StorageItem(gasPerBlock)); entry.Set(gasPerBlock); return true; @@ -192,7 +192,7 @@ private bool SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) [ContractMethod(0_01000000, CallFlags.ReadStates)] public BigInteger GetGasPerBlock(StoreView snapshot) { - return GetSortedGasRecords(snapshot, snapshot.PersistingBlock.Index).First().GasPerBlock; + return GetSortedGasRecords(snapshot, snapshot.Height + 1).First().GasPerBlock; } private IEnumerable<(uint Index, BigInteger GasPerBlock)> GetSortedGasRecords(StoreView snapshot, uint end) diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index 3f8f5e7567..92e00c2565 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -136,7 +136,7 @@ internal override void Initialize(ApplicationEngine engine) internal override void PostPersist(ApplicationEngine engine) { (UInt160 Account, BigInteger GAS)[] nodes = null; - foreach (Transaction tx in engine.Snapshot.PersistingBlock.Transactions) + foreach (Transaction tx in engine.PersistingBlock.Transactions) { //Filter the response transactions OracleResponse response = tx.GetAttribute(); @@ -155,7 +155,7 @@ internal override void PostPersist(ApplicationEngine engine) if (list.Count == 0) engine.Snapshot.Storages.Delete(key); //Mint GAS for oracle nodes - nodes ??= RoleManagement.GetDesignatedByRole(engine.Snapshot, Role.Oracle, engine.Snapshot.PersistingBlock.Index).Select(p => (Contract.CreateSignatureRedeemScript(p).ToScriptHash(), BigInteger.Zero)).ToArray(); + nodes ??= RoleManagement.GetDesignatedByRole(engine.Snapshot, Role.Oracle, engine.PersistingBlock.Index).Select(p => (Contract.CreateSignatureRedeemScript(p).ToScriptHash(), BigInteger.Zero)).ToArray(); if (nodes.Length > 0) { int index = (int)(response.Id % (ulong)nodes.Length); diff --git a/src/neo/SmartContract/Native/RoleManagement.cs b/src/neo/SmartContract/Native/RoleManagement.cs index 728f67aab7..6c32ca7c4b 100644 --- a/src/neo/SmartContract/Native/RoleManagement.cs +++ b/src/neo/SmartContract/Native/RoleManagement.cs @@ -43,9 +43,9 @@ private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] node throw new ArgumentOutOfRangeException(nameof(role)); if (!CheckCommittee(engine)) throw new InvalidOperationException(nameof(DesignateAsRole)); - if (engine.Snapshot.PersistingBlock is null) + if (engine.PersistingBlock is null) throw new InvalidOperationException(nameof(DesignateAsRole)); - uint index = engine.Snapshot.PersistingBlock.Index + 1; + uint index = engine.PersistingBlock.Index + 1; var key = CreateStorageKey((byte)role).AddBigEndian(index); if (engine.Snapshot.Storages.Contains(key)) throw new InvalidOperationException(); diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index 99c6497fb7..5a2d01fa30 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -16,7 +16,7 @@ public static ContractState DeployContract(this StoreView snapshot, UInt160 send script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", true, nefFile, manifest); var engine = ApplicationEngine.Create(TriggerType.Application, - sender != null ? new Transaction() { Signers = new Signer[] { new Signer() { Account = sender } } } : null, snapshot, gas); + sender != null ? new Transaction() { Signers = new Signer[] { new Signer() { Account = sender } } } : null, snapshot, null, gas); engine.LoadScript(script.ToArray()); if (engine.Execute() != VMState.HALT) @@ -91,12 +91,12 @@ public static void DeleteContract(this StoreView snapshot, UInt160 hash) public static StackItem Call(this NativeContract contract, StoreView snapshot, string method, params ContractParameter[] args) { - return Call(contract, snapshot, null, method, args); + return Call(contract, snapshot, null, null, method, args); } - public static StackItem Call(this NativeContract contract, StoreView snapshot, IVerifiable container, string method, params ContractParameter[] args) + public static StackItem Call(this NativeContract contract, StoreView snapshot, IVerifiable container, Block persistingBlock, string method, params ContractParameter[] args) { - var engine = ApplicationEngine.Create(TriggerType.Application, container, snapshot); + var engine = ApplicationEngine.Create(TriggerType.Application, container, snapshot, persistingBlock); var contractState = NativeContract.ContractManagement.GetContract(snapshot, contract.Hash); if (contractState == null) throw new InvalidOperationException(); diff --git a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs index 909ae98d86..4c64606ad4 100644 --- a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs @@ -35,10 +35,10 @@ public void Serialize(BinaryWriter writer) { } public void SerializeUnsigned(BinaryWriter writer) { } } - public static bool Transfer(this NativeContract contract, StoreView snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom) + public static bool Transfer(this NativeContract contract, StoreView snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom, Block persistingBlock) { var engine = ApplicationEngine.Create(TriggerType.Application, - new ManualWitness(signFrom ? new UInt160(from) : null), snapshot); + new ManualWitness(signFrom ? new UInt160(from) : null), snapshot, persistingBlock); engine.LoadScript(contract.Script, pcount: 4, configureState: p => p.ScriptHash = contract.Hash); diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 09904b8c8a..7edab47e07 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -223,7 +223,7 @@ public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() { SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, senderAccount); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, long.MaxValue); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); NativeContract.GAS.Mint(engine, UInt160.Zero, 70, true); @@ -240,7 +240,7 @@ public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() // Simulate the transfer process in tx by burning the balance UInt160 sender = block.Transactions[0].Sender; - ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.All, block, snapshot, (long)balance); + ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.All, block, snapshot, block, (long)balance); NativeContract.GAS.Burn(applicationEngine, sender, NativeContract.GAS.BalanceOf(snapshot, sender)); NativeContract.GAS.Mint(applicationEngine, sender, txFee * 30, true); // Set the balance to meet 30 txs only diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs index 5bc70cef09..2de4f9084a 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs +++ b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs @@ -53,7 +53,7 @@ public void TestDuplicateOracle() // Fake balance var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, long.MaxValue); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, UInt160.Zero); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); NativeContract.GAS.Mint(engine, UInt160.Zero, 8, false); @@ -74,7 +74,7 @@ public void TestDuplicateOracle() public void TestTransactionSenderFee() { var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, long.MaxValue); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, UInt160.Zero); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); NativeContract.GAS.Mint(engine, UInt160.Zero, 8, true); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 14c5afa0e9..16aac9d21d 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -163,7 +163,7 @@ public void FeeIsMultiSigContract() long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -245,7 +245,7 @@ public void FeeIsSignatureContractDetailed() long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -357,7 +357,7 @@ public void FeeIsSignatureContract_TestScope_Global() long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -444,7 +444,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -534,7 +534,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -676,7 +676,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -1026,7 +1026,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs index fdce98c6dd..0b9f8802c6 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -18,6 +18,7 @@ namespace Neo.UnitTests.SmartContract.Native public class UT_GasToken { private StoreView _snapshot; + private Block _persistingBlock; [TestInitialize] public void TestSetup() @@ -25,7 +26,7 @@ public void TestSetup() TestBlockchain.InitializeMockNeoSystem(); _snapshot = Blockchain.Singleton.GetSnapshot(); - _snapshot.PersistingBlock = new Block() { Index = 0 }; + _persistingBlock = new Block() { Index = 0 }; } [TestMethod] @@ -41,26 +42,22 @@ public void TestSetup() public void Check_BalanceOfTransferAndBurn() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block() { Index = 1000 }; - + var persistingBlock = new Block() { Index = 1000 }; byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); - byte[] to = new byte[20]; - var keyCount = snapshot.Storages.GetChangeSet().Count(); - var supply = NativeContract.GAS.TotalSupply(snapshot); supply.Should().Be(3000000050000000); // 3000000000000000 + 50000000 (neo holder reward) // Check unclaim - var unclaim = UT_NeoToken.Check_UnclaimedGas(snapshot, from); + var unclaim = UT_NeoToken.Check_UnclaimedGas(snapshot, from, persistingBlock); unclaim.Value.Should().Be(new BigInteger(0.5 * 1000 * 100000000L)); unclaim.State.Should().BeTrue(); // Transfer - NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.Zero, true).Should().BeTrue(); + NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.Zero, true, persistingBlock).Should().BeTrue(); NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(100000000); NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(0); @@ -69,7 +66,7 @@ public void Check_BalanceOfTransferAndBurn() // Check unclaim - unclaim = UT_NeoToken.Check_UnclaimedGas(snapshot, from); + unclaim = UT_NeoToken.Check_UnclaimedGas(snapshot, from, persistingBlock); unclaim.Value.Should().Be(new BigInteger(0)); unclaim.State.Should().BeTrue(); @@ -82,9 +79,9 @@ public void Check_BalanceOfTransferAndBurn() keyCount = snapshot.Storages.GetChangeSet().Count(); - NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000000, false).Should().BeFalse(); // Not signed - NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000001, true).Should().BeFalse(); // More than balance - NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000000, true).Should().BeTrue(); // All balance + NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000000, false, persistingBlock).Should().BeFalse(); // Not signed + NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000001, true, persistingBlock).Should().BeFalse(); // More than balance + NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000000, true, persistingBlock).Should().BeTrue(); // All balance // Balance of @@ -95,7 +92,7 @@ public void Check_BalanceOfTransferAndBurn() // Burn - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, 0); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, 0); keyCount = snapshot.Storages.GetChangeSet().Count(); Assert.ThrowsException(() => @@ -122,15 +119,15 @@ public void Check_BalanceOfTransferAndBurn() // Bad inputs - NativeContract.GAS.Transfer(snapshot, from, to, BigInteger.MinusOne, true).Should().BeFalse(); - NativeContract.GAS.Transfer(snapshot, new byte[19], to, BigInteger.One, false).Should().BeFalse(); - NativeContract.GAS.Transfer(snapshot, from, new byte[19], BigInteger.One, false).Should().BeFalse(); + NativeContract.GAS.Transfer(snapshot, from, to, BigInteger.MinusOne, true, persistingBlock).Should().BeFalse(); + NativeContract.GAS.Transfer(snapshot, new byte[19], to, BigInteger.One, false, persistingBlock).Should().BeFalse(); + NativeContract.GAS.Transfer(snapshot, from, new byte[19], BigInteger.One, false, persistingBlock).Should().BeFalse(); } [TestMethod] public void Check_BadScript() { - var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); + var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), _persistingBlock, 0); var script = new ScriptBuilder(); script.Emit(OpCode.NOP); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index c473829357..eed8e77026 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -26,7 +26,7 @@ public void TestSetup() [TestMethod] public void TestInitialize() { - ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, 0); + ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0); testNativeContract.Initialize(ae); } @@ -98,7 +98,7 @@ public void TestGetContract() public void TestInvoke() { var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, 0); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, null, 0); engine.LoadScript(testNativeContract.Script, configureState: p => p.ScriptHash = testNativeContract.Hash); ByteString method1 = new ByteString(System.Text.Encoding.Default.GetBytes("wrongMethod")); @@ -119,10 +119,10 @@ public void TestTrigger() { var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine1 = ApplicationEngine.Create(TriggerType.Application, null, snapshot, 0); + ApplicationEngine engine1 = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, 0); Assert.ThrowsException(() => testNativeContract.TestTrigger(engine1)); - ApplicationEngine engine2 = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, 0); + ApplicationEngine engine2 = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, null, 0); testNativeContract.TestTrigger(engine2); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 1bf90d305d..024641eff3 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -23,13 +23,14 @@ namespace Neo.UnitTests.SmartContract.Native public class UT_NeoToken { private StoreView _snapshot; + private Block _persistingBlock; [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); _snapshot = Blockchain.Singleton.GetSnapshot(); - _snapshot.PersistingBlock = new Block() { Index = 0, Transactions = Array.Empty(), ConsensusData = new ConsensusData() }; + _persistingBlock = new Block() { Index = 0, Transactions = Array.Empty(), ConsensusData = new ConsensusData() }; } [TestMethod] @@ -45,25 +46,25 @@ public void TestSetup() public void Check_Vote() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block() { Index = 1000 }; byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); // No signature - var ret = Check_Vote(snapshot, from, null, false); + var ret = Check_Vote(snapshot, from, null, false, persistingBlock); ret.Result.Should().BeFalse(); ret.State.Should().BeTrue(); // Wrong address - ret = Check_Vote(snapshot, new byte[19], null, false); + ret = Check_Vote(snapshot, new byte[19], null, false, persistingBlock); ret.Result.Should().BeFalse(); ret.State.Should().BeFalse(); // Wrong ec - ret = Check_Vote(snapshot, from, new byte[19], true); + ret = Check_Vote(snapshot, from, new byte[19], true, persistingBlock); ret.Result.Should().BeFalse(); ret.State.Should().BeFalse(); @@ -73,7 +74,7 @@ public void Check_Vote() fakeAddr[0] = 0x5F; fakeAddr[5] = 0xFF; - ret = Check_Vote(snapshot, fakeAddr, null, true); + ret = Check_Vote(snapshot, fakeAddr, null, true, persistingBlock); ret.Result.Should().BeFalse(); ret.State.Should().BeTrue(); @@ -81,7 +82,7 @@ public void Check_Vote() var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, from)).GetInteroperable(); accountState.VoteTo = null; - ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true); + ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeFalse(); ret.State.Should().BeTrue(); accountState.VoteTo.Should().BeNull(); @@ -89,7 +90,7 @@ public void Check_Vote() // normal case snapshot.Storages.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); - ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true); + ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); @@ -99,13 +100,13 @@ public void Check_Vote() public void Check_Vote_Sameaccounts() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block() { Index = 1000 }; byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, from)).GetInteroperable(); accountState.Balance = 100; snapshot.Storages.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); - var ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true); + var ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); @@ -116,7 +117,7 @@ public void Check_Vote_Sameaccounts() var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); var secondAccount = snapshot.Storages.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); - ret = Check_Vote(snapshot, G_Account, ECCurve.Secp256r1.G.ToArray(), true); + ret = Check_Vote(snapshot, G_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); stateValidator.Votes.Should().Be(300); @@ -126,7 +127,7 @@ public void Check_Vote_Sameaccounts() public void Check_Vote_ChangeVote() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block() { Index = 1000 }; //from vote to G byte[] from = Blockchain.StandbyValidators[0].ToArray(); var from_Account = Contract.CreateSignatureContract(Blockchain.StandbyValidators[0]).ScriptHash.ToArray(); @@ -134,7 +135,7 @@ public void Check_Vote_ChangeVote() var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.Balance = 100; snapshot.Storages.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); - var ret = Check_Vote(snapshot, from_Account, ECCurve.Secp256r1.G.ToArray(), true); + var ret = Check_Vote(snapshot, from_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); @@ -145,7 +146,7 @@ public void Check_Vote_ChangeVote() var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); snapshot.Storages.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState())); - ret = Check_Vote(snapshot, from_Account, from, true); + ret = Check_Vote(snapshot, from_Account, from, true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); G_stateValidator.Votes.Should().Be(0); @@ -157,7 +158,7 @@ public void Check_Vote_ChangeVote() public void Check_Vote_VoteToNull() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block() { Index = 1000 }; byte[] from = Blockchain.StandbyValidators[0].ToArray(); var from_Account = Contract.CreateSignatureContract(Blockchain.StandbyValidators[0]).ScriptHash.ToArray(); @@ -165,7 +166,7 @@ public void Check_Vote_VoteToNull() var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.Balance = 100; snapshot.Storages.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); - var ret = Check_Vote(snapshot, from_Account, ECCurve.Secp256r1.G.ToArray(), true); + var ret = Check_Vote(snapshot, from_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); @@ -176,7 +177,7 @@ public void Check_Vote_VoteToNull() var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); snapshot.Storages.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState())); - ret = Check_Vote(snapshot, from_Account, null, true); + ret = Check_Vote(snapshot, from_Account, null, true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); G_stateValidator.Votes.Should().Be(0); @@ -187,15 +188,15 @@ public void Check_Vote_VoteToNull() public void Check_UnclaimedGas() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block() { Index = 1000 }; byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); - var unclaim = Check_UnclaimedGas(snapshot, from); + var unclaim = Check_UnclaimedGas(snapshot, from, persistingBlock); unclaim.Value.Should().Be(new BigInteger(0.5 * 1000 * 100000000L)); unclaim.State.Should().BeTrue(); - unclaim = Check_UnclaimedGas(snapshot, new byte[19]); + unclaim = Check_UnclaimedGas(snapshot, new byte[19], persistingBlock); unclaim.Value.Should().Be(BigInteger.Zero); unclaim.State.Should().BeFalse(); } @@ -208,14 +209,14 @@ public void Check_RegisterValidator() var keyCount = snapshot.Storages.GetChangeSet().Count(); var point = Blockchain.StandbyValidators[0].EncodePoint(true).Clone() as byte[]; - var ret = Check_RegisterValidator(snapshot, point); // Exists + var ret = Check_RegisterValidator(snapshot, point, _persistingBlock); // Exists ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); snapshot.Storages.GetChangeSet().Count().Should().Be(++keyCount); // No changes point[20]++; // fake point - ret = Check_RegisterValidator(snapshot, point); // New + ret = Check_RegisterValidator(snapshot, point, _persistingBlock); // New ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); @@ -237,13 +238,13 @@ public void Check_UnregisterCandidate() var point = Blockchain.StandbyValidators[0].EncodePoint(true); //without register - var ret = Check_UnregisterCandidate(snapshot, point); + var ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount); //register and then unregister - ret = Check_RegisterValidator(snapshot, point); + ret = Check_RegisterValidator(snapshot, point, _persistingBlock); StorageItem item = snapshot.Storages.GetAndChange(CreateStorageKey(33, point)); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); @@ -254,7 +255,7 @@ public void Check_UnregisterCandidate() StorageKey key = CreateStorageKey(33, point); snapshot.Storages.TryGet(key).Should().NotBeNull(); - ret = Check_UnregisterCandidate(snapshot, point); + ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount); @@ -264,13 +265,13 @@ public void Check_UnregisterCandidate() snapshot.Storages.TryGet(key).Should().BeNull(); //register with votes, then unregister - ret = Check_RegisterValidator(snapshot, point); + ret = Check_RegisterValidator(snapshot, point, _persistingBlock); var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState())); var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); accountState.Balance = 100; - Check_Vote(snapshot, G_Account, Blockchain.StandbyValidators[0].ToArray(), true); - ret = Check_UnregisterCandidate(snapshot, point); + Check_Vote(snapshot, G_Account, Blockchain.StandbyValidators[0].ToArray(), true, _persistingBlock); + ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); snapshot.Storages.TryGet(key).Should().NotBeNull(); @@ -280,7 +281,7 @@ public void Check_UnregisterCandidate() pointState.Votes.Should().Be(100); //vote fail - ret = Check_Vote(snapshot, G_Account, Blockchain.StandbyValidators[0].ToArray(), true); + ret = Check_Vote(snapshot, G_Account, Blockchain.StandbyValidators[0].ToArray(), true, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); accountState.VoteTo.Should().Be(Blockchain.StandbyValidators[0]); @@ -292,16 +293,17 @@ public void Check_GetCommittee() var snapshot = _snapshot.Clone(); var keyCount = snapshot.Storages.GetChangeSet().Count(); var point = Blockchain.StandbyValidators[0].EncodePoint(true); + var persistingBlock = _persistingBlock; //register with votes with 20000000 var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState())); var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); accountState.Balance = 20000000; - var ret = Check_RegisterValidator(snapshot, ECCurve.Secp256r1.G.ToArray()); + var ret = Check_RegisterValidator(snapshot, ECCurve.Secp256r1.G.ToArray(), persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); - ret = Check_Vote(snapshot, G_Account, ECCurve.Secp256r1.G.ToArray(), true); + ret = Check_Vote(snapshot, G_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); @@ -314,7 +316,7 @@ public void Check_GetCommittee() } //register more candidates, committee member change - snapshot.PersistingBlock = new Block + persistingBlock = new Block { Index = (uint)ProtocolSettings.Default.CommitteeMembersCount, Transactions = Array.Empty(), @@ -322,10 +324,10 @@ public void Check_GetCommittee() }; for (int i = 0; i < ProtocolSettings.Default.CommitteeMembersCount - 1; i++) { - Check_RegisterValidator(snapshot, Blockchain.StandbyCommittee[i].ToArray()); + Check_RegisterValidator(snapshot, Blockchain.StandbyCommittee[i].ToArray(), persistingBlock); } - Check_OnPersist(snapshot); + Check_OnPersist(snapshot, persistingBlock); committeemembers = NativeContract.NEO.GetCommittee(snapshot); committeemembers.Length.Should().Be(ProtocolSettings.Default.CommitteeMembersCount); @@ -341,7 +343,7 @@ public void Check_GetCommittee() public void Check_Transfer() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block() { Index = 1000 }; byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); byte[] to = new byte[20]; @@ -350,20 +352,20 @@ public void Check_Transfer() // Check unclaim - var unclaim = Check_UnclaimedGas(snapshot, from); + var unclaim = Check_UnclaimedGas(snapshot, from, persistingBlock); unclaim.Value.Should().Be(new BigInteger(0.5 * 1000 * 100000000L)); unclaim.State.Should().BeTrue(); // Transfer - NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.One, false).Should().BeFalse(); // Not signed - NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.One, true).Should().BeTrue(); + NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.One, false, persistingBlock).Should().BeFalse(); // Not signed + NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.One, true, persistingBlock).Should().BeTrue(); NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(99999999); NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(1); // Check unclaim - unclaim = Check_UnclaimedGas(snapshot, from); + unclaim = Check_UnclaimedGas(snapshot, from, persistingBlock); unclaim.Value.Should().Be(new BigInteger(0)); unclaim.State.Should().BeTrue(); @@ -373,19 +375,19 @@ public void Check_Transfer() keyCount = snapshot.Storages.GetChangeSet().Count(); - NativeContract.NEO.Transfer(snapshot, to, from, BigInteger.One, true).Should().BeTrue(); + NativeContract.NEO.Transfer(snapshot, to, from, BigInteger.One, true, persistingBlock).Should().BeTrue(); NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(0); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount - 1); // Remove neo balance from address two // Bad inputs - NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.MinusOne, true).Should().BeFalse(); - NativeContract.NEO.Transfer(snapshot, new byte[19], to, BigInteger.One, false).Should().BeFalse(); - NativeContract.NEO.Transfer(snapshot, from, new byte[19], BigInteger.One, false).Should().BeFalse(); + NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.MinusOne, true, persistingBlock).Should().BeFalse(); + NativeContract.NEO.Transfer(snapshot, new byte[19], to, BigInteger.One, false, persistingBlock).Should().BeFalse(); + NativeContract.NEO.Transfer(snapshot, from, new byte[19], BigInteger.One, false, persistingBlock).Should().BeFalse(); // More than balance - NativeContract.NEO.Transfer(snapshot, to, from, new BigInteger(2), true).Should().BeFalse(); + NativeContract.NEO.Transfer(snapshot, to, from, new BigInteger(2), true, persistingBlock).Should().BeFalse(); } [TestMethod] @@ -405,9 +407,9 @@ public void Check_BalanceOf() public void Check_CommitteeBonus() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block { Index = 1, Transactions = Array.Empty() }; + var persistingBlock = new Block { Index = 1, Transactions = Array.Empty() }; - Check_PostPersist(snapshot); + Check_PostPersist(snapshot, persistingBlock); var committee = Blockchain.StandbyCommittee; NativeContract.GAS.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[0]).ScriptHash.ToArray()).Should().Be(50000000); @@ -422,13 +424,13 @@ public void Check_Initialize() // StandbyValidators - Check_GetCommittee(snapshot); + Check_GetCommittee(snapshot, null); } [TestMethod] public void Check_BadScript() { - var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); + var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), _persistingBlock); var script = new ScriptBuilder(); script.Emit(OpCode.NOP); @@ -441,7 +443,7 @@ public void Check_BadScript() public void TestCalculateBonus() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block { Index = 0 }; + var persistingBlock = new Block { Index = 0 }; StorageKey key = CreateStorageKey(20, UInt160.Zero.ToArray()); @@ -578,15 +580,15 @@ public void TestCheckCandidate() snapshot.Storages.Find(storageKey.ToArray()).ToArray().Length.Should().Be(1); // Pre-persist - snapshot.PersistingBlock = new Block { Index = 21, Transactions = Array.Empty() }; - Check_OnPersist(snapshot); + var persistingBlock = new Block { Index = 21, Transactions = Array.Empty() }; + Check_OnPersist(snapshot, persistingBlock); // Clear votes storageKey = new KeyBuilder(-1, 33).Add(committee[0]); snapshot.Storages.GetAndChange(storageKey).GetInteroperable().Votes = BigInteger.Zero; // Unregister candidate, remove - var ret = Check_UnregisterCandidate(snapshot, point); + var ret = Check_UnregisterCandidate(snapshot, point, persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); @@ -594,7 +596,7 @@ public void TestCheckCandidate() snapshot.Storages.Find(storageKey.ToArray()).ToArray().Length.Should().Be(0); // Post-persist - Check_PostPersist(snapshot).Should().BeTrue(); + Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); storageKey = new KeyBuilder(-1, 23).Add(committee[0]); snapshot.Storages.Find(storageKey.ToArray()).ToArray().Length.Should().Be(1); @@ -673,19 +675,19 @@ public void TestTotalSupply() public void TestEconomicParameter() { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = new Block { Index = 0 }; + var persistingBlock = new Block { Index = 0 }; - (BigInteger, bool) result = Check_GetGasPerBlock(snapshot); + (BigInteger, bool) result = Check_GetGasPerBlock(snapshot, persistingBlock); result.Item2.Should().BeTrue(); result.Item1.Should().Be(5 * NativeContract.GAS.Factor); - snapshot.PersistingBlock = new Block { Index = 10 }; - (VM.Types.Boolean, bool) result1 = Check_SetGasPerBlock(snapshot, 10 * NativeContract.GAS.Factor); + persistingBlock = new Block { Index = 10 }; + (VM.Types.Boolean, bool) result1 = Check_SetGasPerBlock(snapshot, 10 * NativeContract.GAS.Factor, persistingBlock); result1.Item2.Should().BeTrue(); result1.Item1.GetBoolean().Should().BeTrue(); - snapshot.PersistingBlock.Index++; - result = Check_GetGasPerBlock(snapshot); + snapshot.BlockHashIndex.GetAndChange().Index = persistingBlock.Index + 1; + result = Check_GetGasPerBlock(snapshot, persistingBlock); result.Item2.Should().BeTrue(); result.Item1.Should().Be(10 * NativeContract.GAS.Factor); @@ -694,7 +696,8 @@ public void TestEconomicParameter() NeoAccountState state = storage.GetInteroperable(); state.Balance = 1000; state.BalanceHeight = 0; - NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, snapshot.PersistingBlock.Index + 1).Should().Be(6500); + snapshot.BlockHashIndex.GetAndChange().Index = 0; // Fake Height=0 + NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, persistingBlock.Index + 2).Should().Be(6500); } [TestMethod] @@ -722,8 +725,8 @@ public void TestClaimGas() var item = snapshot.Storages.GetAndChange(new KeyBuilder(-1, 1), () => new StorageItem()); item.Value = ((BigInteger)2100 * 10000L).ToByteArray(); - snapshot.PersistingBlock = new Block { Index = 0, Transactions = Array.Empty() }; - Check_PostPersist(snapshot).Should().BeTrue(); + var persistingBlock = new Block { Index = 0, Transactions = Array.Empty() }; + Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); var committee = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray(); var accountA = committee[0]; @@ -737,8 +740,8 @@ public void TestClaimGas() // Next block - snapshot.PersistingBlock = new Block { Index = 1, Transactions = Array.Empty() }; - Check_PostPersist(snapshot).Should().BeTrue(); + persistingBlock = new Block { Index = 1, Transactions = Array.Empty() }; + Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[1]).ScriptHash).Should().Be(0); @@ -747,8 +750,8 @@ public void TestClaimGas() // Next block - snapshot.PersistingBlock = new Block { Index = 21, Transactions = Array.Empty() }; - Check_PostPersist(snapshot).Should().BeTrue(); + persistingBlock = new Block { Index = 21, Transactions = Array.Empty() }; + Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); accountA = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray()[2]; NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[2]).ScriptHash).Should().Be(0); @@ -786,16 +789,16 @@ public void TestVote() UInt160 account = UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4"); StorageKey keyAccount = CreateStorageKey(20, account.ToArray()); StorageKey keyValidator = CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()); - var ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), false); + var ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), false, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); - ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); + ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); snapshot.Storages.Add(keyAccount, new StorageItem(new NeoAccountState())); - ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); + ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); @@ -805,7 +808,7 @@ public void TestVote() VoteTo = ECCurve.Secp256r1.G })); snapshot.Storages.Add(keyValidator, new StorageItem(new CandidateState())); - ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); + ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); } @@ -813,8 +816,7 @@ public void TestVote() internal (bool State, bool Result) Transfer4TesingOnBalanceChanging(BigInteger amount, bool addVotes) { var snapshot = _snapshot.Clone(); - snapshot.PersistingBlock = Blockchain.GenesisBlock; - var engine = ApplicationEngine.Create(TriggerType.Application, Blockchain.GenesisBlock, snapshot); + var engine = ApplicationEngine.Create(TriggerType.Application, Blockchain.GenesisBlock, snapshot, Blockchain.GenesisBlock); ScriptBuilder sb = new ScriptBuilder(); var tmp = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); UInt160 from = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot)[0]; @@ -843,29 +845,29 @@ public void TestVote() return (true, result.GetBoolean()); } - internal static bool Check_OnPersist(StoreView snapshot) + internal static bool Check_OnPersist(StoreView snapshot, Block persistingBlock) { var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_NativeOnPersist); - var engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot); + var engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, persistingBlock); engine.LoadScript(script.ToArray()); return engine.Execute() == VMState.HALT; } - internal static bool Check_PostPersist(StoreView snapshot) + internal static bool Check_PostPersist(StoreView snapshot, Block persistingBlock) { var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_NativePostPersist); - var engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot); + var engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, persistingBlock); engine.LoadScript(script.ToArray()); return engine.Execute() == VMState.HALT; } - internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView snapshot) + internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView snapshot, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 0, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); @@ -884,10 +886,10 @@ internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView sn return (((VM.Types.Integer)result).GetInteger(), true); } - internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreView snapshot, BigInteger gasPerBlock) + internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreView snapshot, BigInteger gasPerBlock, Block persistingBlock) { UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot); + var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); @@ -907,10 +909,10 @@ internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreV return (((VM.Types.Boolean)result).GetBoolean(), true); } - internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[] pubkey, bool signAccount) + internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[] pubkey, bool signAccount, Block persistingBlock) { var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot); + new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 2, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); @@ -935,10 +937,10 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] return (true, result.GetBoolean()); } - internal static (bool State, bool Result) Check_RegisterValidator(StoreView snapshot, byte[] pubkey) + internal static (bool State, bool Result) Check_RegisterValidator(StoreView snapshot, byte[] pubkey, Block persistingBlock) { var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot); + new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); @@ -958,9 +960,9 @@ internal static (bool State, bool Result) Check_RegisterValidator(StoreView snap return (true, result.GetBoolean()); } - internal static ECPoint[] Check_GetCommittee(StoreView snapshot) + internal static ECPoint[] Check_GetCommittee(StoreView snapshot, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 0, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); @@ -976,14 +978,14 @@ internal static ECPoint[] Check_GetCommittee(StoreView snapshot) return (result as VM.Types.Array).Select(u => u.GetSpan().AsSerializable()).ToArray(); } - internal static (BigInteger Value, bool State) Check_UnclaimedGas(StoreView snapshot, byte[] address) + internal static (BigInteger Value, bool State) Check_UnclaimedGas(StoreView snapshot, byte[] address, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 2, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); var script = new ScriptBuilder(); - script.EmitPush(snapshot.PersistingBlock.Index); + script.EmitPush(persistingBlock.Index); script.EmitPush(address); script.EmitPush("unclaimedGas"); engine.LoadScript(script.ToArray()); @@ -1035,10 +1037,10 @@ internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) return storageKey; } - internal static (bool State, bool Result) Check_UnregisterCandidate(StoreView snapshot, byte[] pubkey) + internal static (bool State, bool Result) Check_UnregisterCandidate(StoreView snapshot, byte[] pubkey, Block persistingBlock) { var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot); + new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 888dbdc233..d0dee61fc8 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -22,9 +22,8 @@ public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); _snapshot = Blockchain.Singleton.GetSnapshot(); - _snapshot.PersistingBlock = new Block() { Index = 0 }; - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, _snapshot.PersistingBlock, _snapshot, 0); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, _snapshot, new Block(), 0); NativeContract.ContractManagement.OnPersist(engine); } @@ -57,14 +56,14 @@ public void Check_SetMaxBlockSize() // Fake blockchain - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), block, "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -77,7 +76,7 @@ public void Check_SetMaxBlockSize() Assert.ThrowsException(() => { - NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = Neo.Network.P2P.Message.PayloadMaxSize + 1 }); }); @@ -87,7 +86,7 @@ public void Check_SetMaxBlockSize() // With signature - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); @@ -104,14 +103,14 @@ public void Check_SetMaxBlockSystemFee() // Fake blockchain - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), block, "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = 1024 * (long)NativeContract.GAS.Factor }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -124,7 +123,7 @@ public void Check_SetMaxBlockSystemFee() Assert.ThrowsException(() => { - NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = -1000 }); }); @@ -134,7 +133,7 @@ public void Check_SetMaxBlockSystemFee() // With signature - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = 1024 * (long)NativeContract.GAS.Factor }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); @@ -151,12 +150,12 @@ public void Check_SetMaxTransactionsPerBlock() // Fake blockchain - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new TrimmedBlock() { NextConsensus = UInt160.Zero }); // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -167,7 +166,7 @@ public void Check_SetMaxTransactionsPerBlock() // With signature - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(NativeContract.NEO.GetCommitteeAddress(snapshot)), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(NativeContract.NEO.GetCommitteeAddress(snapshot)), block, "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); @@ -184,12 +183,12 @@ public void Check_SetFeePerByte() // Fake blockchain - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new TrimmedBlock() { NextConsensus = UInt160.Zero }); // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "setFeePerByte", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -200,7 +199,7 @@ public void Check_SetFeePerByte() // With signature UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setFeePerByte", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); @@ -217,14 +216,14 @@ public void Check_SetBaseExecFee() // Fake blockchain - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - NativeContract.Policy.Initialize(ApplicationEngine.Create(TriggerType.Application, null, snapshot, 0)); + NativeContract.Policy.Initialize(ApplicationEngine.Create(TriggerType.Application, null, snapshot, block, 0)); // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "setExecFeeFactor", new ContractParameter(ContractParameterType.Integer) { Value = 50 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -237,7 +236,7 @@ public void Check_SetBaseExecFee() UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); Assert.ThrowsException(() => { - NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setExecFeeFactor", new ContractParameter(ContractParameterType.Integer) { Value = 100500 }); }); @@ -246,7 +245,7 @@ public void Check_SetBaseExecFee() ret.GetInteger().Should().Be(30); // Proper set - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setExecFeeFactor", new ContractParameter(ContractParameterType.Integer) { Value = 50 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); @@ -263,14 +262,14 @@ public void Check_SetStoragePrice() // Fake blockchain - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - NativeContract.Policy.Initialize(ApplicationEngine.Create(TriggerType.Application, null, snapshot, 0)); + NativeContract.Policy.Initialize(ApplicationEngine.Create(TriggerType.Application, null, snapshot, block, 0)); // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "setStoragePrice", new ContractParameter(ContractParameterType.Integer) { Value = 100500 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -283,7 +282,7 @@ public void Check_SetStoragePrice() UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); Assert.ThrowsException(() => { - NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setStoragePrice", new ContractParameter(ContractParameterType.Integer) { Value = 100000000 }); }); @@ -292,7 +291,7 @@ public void Check_SetStoragePrice() ret.GetInteger().Should().Be(100000); // Proper set - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setStoragePrice", new ContractParameter(ContractParameterType.Integer) { Value = 300300 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); @@ -309,12 +308,12 @@ public void Check_BlockAccount() // Fake blockchain - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new TrimmedBlock() { NextConsensus = UInt160.Zero }); // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), block, "blockAccount", new ContractParameter(ContractParameterType.ByteArray) { Value = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01").ToArray() }); ret.Should().BeOfType(); @@ -323,14 +322,14 @@ public void Check_BlockAccount() // With signature UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "blockAccount", new ContractParameter(ContractParameterType.ByteArray) { Value = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01").ToArray() }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); // Same account - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "blockAccount", new ContractParameter(ContractParameterType.ByteArray) { Value = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01").ToArray() }); ret.Should().BeOfType(); @@ -338,7 +337,7 @@ public void Check_BlockAccount() // Account B - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "blockAccount", new ContractParameter(ContractParameterType.ByteArray) { Value = UInt160.Parse("0xb400ff00ff00ff00ff00ff00ff00ff00ff00ff01").ToArray() }); ret.Should().BeOfType(); @@ -358,14 +357,14 @@ public void Check_Block_UnblockAccount() // Fake blockchain - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new TrimmedBlock() { NextConsensus = UInt160.Zero }); UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); // Block without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "blockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -374,7 +373,7 @@ public void Check_Block_UnblockAccount() // Block with signature - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "blockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); @@ -383,7 +382,7 @@ public void Check_Block_UnblockAccount() // Unblock without signature - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "unblockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -392,7 +391,7 @@ public void Check_Block_UnblockAccount() // Unblock with signature - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "unblockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs index bbee62a469..24db8906f5 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs @@ -23,22 +23,18 @@ public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); _snapshot = Blockchain.Singleton.GetSnapshot(); - _snapshot.PersistingBlock = new Block() { Index = 0 }; } [TestMethod] public void TestSetAndGet() { var snapshot1 = _snapshot.Clone(); - snapshot1.PersistingBlock = new Block - { - Index = 0, - }; UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot1); ECPoint[] validators = NativeContract.NEO.ComputeNextBlockValidators(snapshot1); var ret = NativeContract.RoleManagement.Call( snapshot1, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + new Block(), "designateAsRole", new ContractParameter(ContractParameterType.Integer) { Value = new BigInteger((int)Role.StateValidator) }, new ContractParameter(ContractParameterType.Array) { Value = validators.Select(p => new ContractParameter(ContractParameterType.ByteArray) { Value = p.ToArray() }).ToList() } diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index 36a67f026c..be99c15830 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -95,10 +95,10 @@ public void TestCreateDummyBlock() { var snapshot = Blockchain.Singleton.GetSnapshot(); byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; - ApplicationEngine.Run(SyscallSystemRuntimeCheckWitnessHash, snapshot); - snapshot.PersistingBlock.Version.Should().Be(0); - snapshot.PersistingBlock.PrevHash.Should().Be(Blockchain.GenesisBlock.Hash); - snapshot.PersistingBlock.MerkleRoot.Should().Be(new UInt256()); + ApplicationEngine engine = ApplicationEngine.Run(SyscallSystemRuntimeCheckWitnessHash, snapshot); + engine.PersistingBlock.Version.Should().Be(0); + engine.PersistingBlock.PrevHash.Should().Be(Blockchain.GenesisBlock.Hash); + engine.PersistingBlock.MerkleRoot.Should().Be(new UInt256()); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs index ccfd7ff138..ad8e37b107 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs @@ -28,14 +28,14 @@ public void TestSetAppEngineProvider() var provider = new TestProvider(); ApplicationEngine.SetApplicationEngineProvider(provider).Should().BeTrue(); - using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, 0); + using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0); (appEngine is TestEngine).Should().BeTrue(); } [TestMethod] public void TestDefaultAppEngineProvider() { - using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, 0); + using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0); (appEngine is ApplicationEngine).Should().BeTrue(); } @@ -63,16 +63,16 @@ public void TestCanResetAppEngineProviderTwice() class TestProvider : IApplicationEngineProvider { - public ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas) + public ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock, long gas) { - return new TestEngine(trigger, container, snapshot, gas); + return new TestEngine(trigger, container, snapshot, persistingBlock, gas); } } class TestEngine : ApplicationEngine { - public TestEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas) - : base(trigger, container, snapshot, gas) + public TestEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock, long gas) + : base(trigger, container, snapshot, persistingBlock, gas) { } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index cdcd78247f..53b8f559af 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -21,7 +21,7 @@ public void ApplicationEngineFixedPrices() { // System.Runtime.CheckWitness: f827ec8c (price is 200) byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; - using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, 0)) + using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0)) { ae.LoadScript(SyscallSystemRuntimeCheckWitnessHash); ApplicationEngine.System_Runtime_CheckWitness.FixedPrice.Should().Be(0_00001024L); @@ -29,7 +29,7 @@ public void ApplicationEngineFixedPrices() // System.Storage.GetContext: 9bf667ce (price is 1) byte[] SyscallSystemStorageGetContextHash = new byte[] { 0x68, 0x9b, 0xf6, 0x67, 0xce }; - using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, 0)) + using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetContextHash); ApplicationEngine.System_Storage_GetContext.FixedPrice.Should().Be(0_00000016L); @@ -37,7 +37,7 @@ public void ApplicationEngineFixedPrices() // System.Storage.Get: 925de831 (price is 100) byte[] SyscallSystemStorageGetHash = new byte[] { 0x68, 0x92, 0x5d, 0xe8, 0x31 }; - using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, 0)) + using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetHash); ApplicationEngine.System_Storage_Get.FixedPrice.Should().Be(32768L); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index a45b2bed46..477eccf54e 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -131,7 +131,6 @@ public void TestAccount_IsStandard() public void TestContract_Create() { var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); - snapshot.PersistingBlock = new Block() { }; var nef = new NefFile() { Script = new byte[byte.MaxValue], @@ -177,8 +176,6 @@ public void TestContract_Create() public void TestContract_Update() { var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); - snapshot.PersistingBlock = new Block() { }; - var nef = new NefFile() { Script = new byte[] { 0x01 }, @@ -241,7 +238,6 @@ public void TestContract_Update_Invalid() nefFile.CheckSum = NefFile.ComputeChecksum(nefFile); var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); - snapshot.PersistingBlock = new Block(); Assert.ThrowsException(() => snapshot.UpdateContract(null, null, new byte[] { 0x01 })); Assert.ThrowsException(() => snapshot.UpdateContract(null, nefFile.ToArray(), null)); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 60ff884f44..04dfa63916 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -220,7 +220,7 @@ public void TestExecutionEngine_GetCallingScriptHash() Nef = new NefFile { Script = scriptA.ToArray() }, Hash = scriptA.ToArray().ToScriptHash() }; - engine = GetEngine(true, true, false); + engine = GetEngine(true, true, addScript: false); engine.Snapshot.AddContract(contract.Hash, contract); using ScriptBuilder scriptB = new ScriptBuilder(); @@ -281,9 +281,7 @@ public void TestRuntime_Log() public void TestRuntime_GetTime() { Block block = new Block(); - TestUtils.SetupBlockWithValues(block, UInt256.Zero, out _, out _, out _, out _, out _, out _, 0); - var engine = GetEngine(true, true); - engine.Snapshot.PersistingBlock = block; + var engine = GetEngine(true, true, hasBlock: true); engine.GetTime().Should().Be(block.Timestamp); } @@ -619,8 +617,6 @@ public void TestContract_Call() public void TestContract_Destroy() { var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); - snapshot.PersistingBlock = new Block(); - var state = TestUtils.GetContract(); var scriptHash = UInt160.Parse("0xcb9f3b7c6fb1cf2c13a40637c189bdd066a272b4"); var storageItem = new StorageItem @@ -660,31 +656,13 @@ public static void LogEvent(object sender, LogEventArgs args) tx.Script = new byte[] { 0x01, 0x02, 0x03 }; } - private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSnapshot = false, bool addScript = true, long gas = 20_00000000) + private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSnapshot = false, bool hasBlock = false, bool addScript = true, long gas = 20_00000000) { - var tx = TestUtils.GetTransaction(UInt160.Zero); - var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); - ApplicationEngine engine; - if (hasContainer && hasSnapshot) - { - engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, gas); - } - else if (hasContainer && !hasSnapshot) - { - engine = ApplicationEngine.Create(TriggerType.Application, tx, null, gas); - } - else if (!hasContainer && hasSnapshot) - { - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, gas); - } - else - { - engine = ApplicationEngine.Create(TriggerType.Application, null, null, gas); - } - if (addScript) - { - engine.LoadScript(new byte[] { 0x01 }); - } + var tx = hasContainer ? TestUtils.GetTransaction(UInt160.Zero) : null; + var snapshot = hasSnapshot ? Blockchain.Singleton.GetSnapshot().Clone() : null; + var block = hasBlock ? new Block() : null; + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, block, gas); + if (addScript) engine.LoadScript(new byte[] { 0x01 }); return engine; } } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index d39bcf9a8b..029c04c623 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -279,7 +279,7 @@ public void System_Runtime_GasLeft() // Execute - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, 100_000_000); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, 100_000_000); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); From 3ee76ba915ea90b399324a80660a0966e3a593d8 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 10 Jan 2021 13:36:10 +0800 Subject: [PATCH 22/65] LoadToken (#2200) --- .../ApplicationEngine.Contract.cs | 49 ------------ src/neo/SmartContract/ApplicationEngine.cs | 76 +++++++++++++++++-- .../SmartContract/ExecutionContextState.cs | 5 ++ .../SmartContract/UT_InteropService.cs | 8 +- 4 files changed, 79 insertions(+), 59 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index cd0df5423d..17dd21fc4f 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -1,8 +1,6 @@ using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; -using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; -using Neo.VM; using Neo.VM.Types; using System; @@ -35,53 +33,6 @@ protected internal void CallContract(UInt160 contractHash, string method, CallFl CallContractInternal(contractHash, method, callFlags, hasReturnValue, args); } - private void CallContractInternal(UInt160 contractHash, string method, CallFlags flags, bool hasReturnValue, StackItem[] args) - { - ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, contractHash); - if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}"); - ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); - if (md is null) throw new InvalidOperationException($"Method {method} Does Not Exist In Contract {contractHash}"); - - if (md.Safe) - { - flags &= ~CallFlags.WriteStates; - } - else - { - ContractState currentContract = NativeContract.ContractManagement.GetContract(Snapshot, CurrentScriptHash); - if (currentContract?.CanCall(contract, method) == false) - throw new InvalidOperationException($"Cannot Call Method {method} Of Contract {contractHash} From Contract {CurrentScriptHash}"); - } - - CallContractInternal(contract, md, flags, hasReturnValue, args); - } - - private void CallContractInternal(ContractState contract, ContractMethodDescriptor method, CallFlags flags, bool hasReturnValue, StackItem[] args) - { - if (invocationCounter.TryGetValue(contract.Hash, out var counter)) - { - invocationCounter[contract.Hash] = counter + 1; - } - else - { - invocationCounter[contract.Hash] = 1; - } - - ExecutionContextState state = CurrentContext.GetState(); - UInt160 callingScriptHash = state.ScriptHash; - CallFlags callingFlags = state.CallFlags; - - if (args.Length != method.Parameters.Length) throw new InvalidOperationException($"Method {method.Name} Expects {method.Parameters.Length} Arguments But Receives {args.Length} Arguments"); - ExecutionContext context_new = LoadContract(contract, method.Name, flags & callingFlags, hasReturnValue, (ushort)args.Length); - state = context_new.GetState(); - state.CallingScriptHash = callingScriptHash; - - for (int i = args.Length - 1; i >= 0; i--) - context_new.EvaluationStack.Push(args[i]); - if (NativeContract.IsNative(contract.Hash)) - context_new.EvaluationStack.Push(method.Name); - } - protected internal void CallNativeContract(string name) { NativeContract contract = NativeContract.GetContract(name); diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 3fa14e5683..883ea6ef7e 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -76,20 +76,69 @@ protected override void OnFault(Exception e) base.OnFault(e); } - internal void CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, string method, params StackItem[] args) + private ExecutionContext CallContractInternal(UInt160 contractHash, string method, CallFlags flags, bool hasReturnValue, StackItem[] args) { - CallContractInternal(hash, method, CallFlags.All, false, args); + ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, contractHash); + if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}"); + ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); + if (md is null) throw new InvalidOperationException($"Method {method} Does Not Exist In Contract {contractHash}"); + + if (md.Safe) + { + flags &= ~CallFlags.WriteStates; + } + else + { + ContractState currentContract = NativeContract.ContractManagement.GetContract(Snapshot, CurrentScriptHash); + if (currentContract?.CanCall(contract, method) == false) + throw new InvalidOperationException($"Cannot Call Method {method} Of Contract {contractHash} From Contract {CurrentScriptHash}"); + } + + if (invocationCounter.TryGetValue(contract.Hash, out var counter)) + { + invocationCounter[contract.Hash] = counter + 1; + } + else + { + invocationCounter[contract.Hash] = 1; + } + ExecutionContextState state = CurrentContext.GetState(); + UInt160 callingScriptHash = state.ScriptHash; + CallFlags callingFlags = state.CallFlags; + + if (args.Length != md.Parameters.Length) throw new InvalidOperationException($"Method {method} Expects {md.Parameters.Length} Arguments But Receives {args.Length} Arguments"); + if (hasReturnValue ^ (md.ReturnType != ContractParameterType.Void)) throw new InvalidOperationException("The return value type does not match."); + ExecutionContext context_new = LoadContract(contract, method, flags & callingFlags, hasReturnValue, (ushort)args.Length); + state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; - StepOut(); + + for (int i = args.Length - 1; i >= 0; i--) + context_new.EvaluationStack.Push(args[i]); + if (NativeContract.IsNative(contract.Hash)) + context_new.EvaluationStack.Push(method); + + return context_new; + } + + internal void CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, string method, params StackItem[] args) + { + ExecutionContext context_current = CurrentContext; + ExecutionContext context_new = CallContractInternal(hash, method, CallFlags.All, false, args); + ExecutionContextState state = context_new.GetState(); + state.CallingScriptHash = callingScriptHash; + while (CurrentContext != context_current) + StepOut(); } internal T CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, string method, params StackItem[] args) { - CallContractInternal(hash, method, CallFlags.All, true, args); - ExecutionContextState state = CurrentContext.GetState(); + ExecutionContext context_current = CurrentContext; + ExecutionContext context_new = CallContractInternal(hash, method, CallFlags.All, true, args); + ExecutionContextState state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; - StepOut(); + while (CurrentContext != context_current) + StepOut(); return (T)Convert(Pop(), new InteropParameterDescriptor(typeof(T))); } @@ -123,6 +172,7 @@ public ExecutionContext LoadContract(ContractState contract, string method, Call { p.CallFlags = callFlags; p.ScriptHash = contract.Hash; + p.Contract = contract; }); // Call initialization @@ -145,6 +195,20 @@ public ExecutionContext LoadScript(Script script, ushort pcount = 0, int rvcount return context; } + protected override ExecutionContext LoadToken(ushort tokenId) + { + ContractState contract = CurrentContext.GetState().Contract; + if (contract is null || tokenId >= contract.Nef.Tokens.Length) + throw new InvalidOperationException(); + MethodToken token = contract.Nef.Tokens[tokenId]; + if (token.ParametersCount > CurrentContext.EvaluationStack.Count) + throw new InvalidOperationException(); + StackItem[] args = new StackItem[token.ParametersCount]; + for (int i = 0; i < token.ParametersCount; i++) + args[i] = Pop(); + return CallContractInternal(token.Hash, token.Method, token.CallFlags, token.HasReturnValue, args); + } + protected internal StackItem Convert(object value) { if (value is IDisposable disposable) Disposables.Add(disposable); diff --git a/src/neo/SmartContract/ExecutionContextState.cs b/src/neo/SmartContract/ExecutionContextState.cs index 0d0250803c..bbbdefbae9 100644 --- a/src/neo/SmartContract/ExecutionContextState.cs +++ b/src/neo/SmartContract/ExecutionContextState.cs @@ -12,6 +12,11 @@ public class ExecutionContextState /// public UInt160 CallingScriptHash { get; set; } + /// + /// The ContractState of the current context. + /// + public ContractState Contract { get; set; } + /// /// Execution context rights /// diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 04dfa63916..2c99d9b7a8 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -597,20 +597,20 @@ public void TestContract_Call() engine.LoadScript(new byte[] { 0x01 }); engine.Push(args[1]); engine.Push(args[0]); - engine.CallContract(state.Hash, method, CallFlags.All, false, (ushort)args.Count); + engine.CallContract(state.Hash, method, CallFlags.All, true, (ushort)args.Count); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); state.Manifest.Permissions[0].Methods = WildcardContainer.Create("a"); engine.Push(args[1]); engine.Push(args[0]); - Assert.ThrowsException(() => engine.CallContract(state.Hash, method, CallFlags.All, false, (ushort)args.Count)); + Assert.ThrowsException(() => engine.CallContract(state.Hash, method, CallFlags.All, true, (ushort)args.Count)); state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); engine.Push(args[1]); engine.Push(args[0]); - engine.CallContract(state.Hash, method, CallFlags.All, false, (ushort)args.Count); + engine.CallContract(state.Hash, method, CallFlags.All, true, (ushort)args.Count); engine.Push(args[1]); engine.Push(args[0]); - Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, method, CallFlags.All, false, (ushort)args.Count)); + Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, method, CallFlags.All, true, (ushort)args.Count)); } [TestMethod] From b87df5d29f8a26582b3824cb5f87ace1fa6578d4 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 10 Jan 2021 19:05:36 +0800 Subject: [PATCH 23/65] Neo Name Service (#2201) --- src/neo/SmartContract/Native/NameService.cs | 285 ++++++++++++++++++ .../SmartContract/Native/NativeContract.cs | 1 + .../SmartContract/Native/NonfungibleToken.cs | 16 +- src/neo/SmartContract/Native/RecordType.cs | 15 + .../SmartContract/Native/UT_NameService.cs | 284 +++++++++++++++++ 5 files changed, 595 insertions(+), 6 deletions(-) create mode 100644 src/neo/SmartContract/Native/NameService.cs create mode 100644 src/neo/SmartContract/Native/RecordType.cs create mode 100644 tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs new file mode 100644 index 0000000000..d6be3d92c1 --- /dev/null +++ b/src/neo/SmartContract/Native/NameService.cs @@ -0,0 +1,285 @@ +#pragma warning disable IDE0051 + +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Persistence; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Numerics; +using System.Text.RegularExpressions; + +namespace Neo.SmartContract.Native +{ + public sealed class NameService : NonfungibleToken + { + public override int Id => -6; + public override string Symbol => "NNS"; + + private const byte Prefix_Roots = 10; + private const byte Prefix_DomainPrice = 22; + private const byte Prefix_Expiration = 20; + private const byte Prefix_Record = 12; + + private const uint OneYear = 365 * 24 * 3600; + private static readonly Regex rootRegex = new Regex("^[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); + private static readonly Regex nameRegex = new Regex("^(?=.{3,255}$)([a-z0-9]{1,62}\\.)+[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); + + internal NameService() + { + } + + internal override void Initialize(ApplicationEngine engine) + { + engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_DomainPrice), new StorageItem(10_00000000)); + } + + internal override void OnPersist(ApplicationEngine engine) + { + uint now = (uint)(engine.PersistingBlock.Timestamp / 1000) + 1; + byte[] start = CreateStorageKey(Prefix_Expiration).AddBigEndian(0).ToArray(); + byte[] end = CreateStorageKey(Prefix_Expiration).AddBigEndian(now).ToArray(); + foreach (var (key, _) in engine.Snapshot.Storages.FindRange(start, end)) + { + engine.Snapshot.Storages.Delete(key); + foreach (var (key2, _) in engine.Snapshot.Storages.Find(CreateStorageKey(Prefix_Record).Add(key.Key.AsSpan(5)).ToArray())) + engine.Snapshot.Storages.Delete(key2); + Burn(engine, CreateStorageKey(Prefix_Token).Add(key.Key.AsSpan(5))); + } + } + + protected override byte[] GetKey(byte[] tokenId) + { + return Crypto.Hash160(tokenId); + } + + [ContractMethod(0_03000000, CallFlags.WriteStates)] + private void AddRoot(ApplicationEngine engine, string root) + { + if (!rootRegex.IsMatch(root)) throw new ArgumentException(null, nameof(root)); + if (!CheckCommittee(engine)) throw new InvalidOperationException(); + StringList roots = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Roots), () => new StorageItem(new StringList())).GetInteroperable(); + int index = roots.BinarySearch(root); + if (index >= 0) throw new InvalidOperationException("The name already exists."); + roots.Insert(~index, root); + } + + public IEnumerable GetRoots(StoreView snapshot) + { + return snapshot.Storages.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable() ?? Enumerable.Empty(); + } + + [ContractMethod(0_03000000, CallFlags.WriteStates)] + private void SetPrice(ApplicationEngine engine, long price) + { + if (price <= 0 || price > 10000_00000000) throw new ArgumentOutOfRangeException(nameof(price)); + if (!CheckCommittee(engine)) throw new InvalidOperationException(); + engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_DomainPrice)).Set(price); + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public long GetPrice(StoreView snapshot) + { + return (long)(BigInteger)snapshot.Storages[CreateStorageKey(Prefix_DomainPrice)]; + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public bool IsAvailable(StoreView snapshot, string name) + { + if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); + string[] names = name.Split('.'); + if (names.Length != 2) throw new ArgumentException(null, nameof(name)); + byte[] hash = GetKey(Utility.StrictUTF8.GetBytes(name)); + if (snapshot.Storages.TryGet(CreateStorageKey(Prefix_Token).Add(hash)) is not null) return false; + StringList roots = snapshot.Storages.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable(); + if (roots is null || roots.BinarySearch(names[1]) < 0) throw new InvalidOperationException(); + return true; + } + + [ContractMethod(0_01000000, CallFlags.WriteStates)] + private bool Register(ApplicationEngine engine, string name, UInt160 owner) + { + if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); + string[] names = name.Split('.'); + if (names.Length != 2) throw new ArgumentException(null, nameof(name)); + if (!engine.CheckWitnessInternal(owner)) throw new InvalidOperationException(); + byte[] hash = GetKey(Utility.StrictUTF8.GetBytes(name)); + if (engine.Snapshot.Storages.TryGet(CreateStorageKey(Prefix_Token).Add(hash)) is not null) return false; + StringList roots = engine.Snapshot.Storages.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable(); + if (roots is null || roots.BinarySearch(names[1]) < 0) throw new InvalidOperationException(); + engine.AddGas(GetPrice(engine.Snapshot)); + NameState state = new NameState + { + Owner = owner, + Name = name, + Description = "", + Expiration = (uint)(engine.PersistingBlock.Timestamp / 1000) + OneYear + }; + Mint(engine, state); + engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash), new StorageItem(new byte[] { 0 })); + return true; + } + + [ContractMethod(0, CallFlags.WriteStates)] + private uint Renew(ApplicationEngine engine, string name) + { + if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); + string[] names = name.Split('.'); + if (names.Length != 2) throw new ArgumentException(null, nameof(name)); + engine.AddGas(GetPrice(engine.Snapshot)); + byte[] hash = GetKey(Utility.StrictUTF8.GetBytes(name)); + NameState state = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Token).Add(hash)).GetInteroperable(); + engine.Snapshot.Storages.Delete(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash)); + state.Expiration += OneYear; + engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash), new StorageItem(new byte[] { 0 })); + return state.Expiration; + } + + [ContractMethod(0_03000000, CallFlags.WriteStates)] + private void SetAdmin(ApplicationEngine engine, string name, UInt160 admin) + { + if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); + string[] names = name.Split('.'); + if (names.Length != 2) throw new ArgumentException(null, nameof(name)); + if (admin != null && !engine.CheckWitnessInternal(admin)) throw new InvalidOperationException(); + NameState state = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Token).Add(GetKey(Utility.StrictUTF8.GetBytes(name)))).GetInteroperable(); + if (!engine.CheckWitnessInternal(state.Owner)) throw new InvalidOperationException(); + state.Admin = admin; + } + + private static bool CheckAdmin(ApplicationEngine engine, NameState state) + { + if (engine.CheckWitnessInternal(state.Owner)) return true; + if (state.Admin is null) return false; + return engine.CheckWitnessInternal(state.Admin); + } + + [ContractMethod(0_30000000, CallFlags.WriteStates)] + private void SetRecord(ApplicationEngine engine, string name, RecordType type, string data) + { + if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); + switch (type) + { + case RecordType.A: + if (!IPAddress.TryParse(data, out IPAddress address)) throw new FormatException(); + if (address.AddressFamily != AddressFamily.InterNetwork) throw new FormatException(); + break; + case RecordType.CNAME: + if (!nameRegex.IsMatch(data)) throw new FormatException(); + break; + case RecordType.TXT: + if (Utility.StrictUTF8.GetByteCount(data) > 255) throw new FormatException(); + break; + case RecordType.AAAA: + if (!IPAddress.TryParse(data, out address)) throw new FormatException(); + if (address.AddressFamily != AddressFamily.InterNetworkV6) throw new FormatException(); + break; + default: + throw new ArgumentOutOfRangeException(nameof(type)); + } + string domain = string.Join('.', name.Split('.')[^2..]); + byte[] hash_domain = GetKey(Utility.StrictUTF8.GetBytes(domain)); + NameState state = engine.Snapshot.Storages[CreateStorageKey(Prefix_Token).Add(hash_domain)].GetInteroperable(); + if (!CheckAdmin(engine, state)) throw new InvalidOperationException(); + StorageItem item = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type), () => new StorageItem()); + item.Value = Utility.StrictUTF8.GetBytes(data); + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public string GetRecord(StoreView snapshot, string name, RecordType type) + { + if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); + string domain = string.Join('.', name.Split('.')[^2..]); + byte[] hash_domain = GetKey(Utility.StrictUTF8.GetBytes(domain)); + StorageItem item = snapshot.Storages.TryGet(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type)); + if (item is null) return null; + return Utility.StrictUTF8.GetString(item.Value); + } + + public IEnumerable<(RecordType Type, string Data)> GetRecords(StoreView snapshot, string name) + { + if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); + string domain = string.Join('.', name.Split('.')[^2..]); + byte[] hash_domain = GetKey(Utility.StrictUTF8.GetBytes(domain)); + foreach (var (key, value) in snapshot.Storages.Find(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).ToArray())) + yield return ((RecordType)key.Key[^1], Utility.StrictUTF8.GetString(value.Value)); + } + + [ContractMethod(0_01000000, CallFlags.WriteStates)] + private void DeleteRecord(ApplicationEngine engine, string name, RecordType type) + { + if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); + string domain = string.Join('.', name.Split('.')[^2..]); + byte[] hash_domain = GetKey(Utility.StrictUTF8.GetBytes(domain)); + NameState state = engine.Snapshot.Storages[CreateStorageKey(Prefix_Token).Add(hash_domain)].GetInteroperable(); + if (!CheckAdmin(engine, state)) throw new InvalidOperationException(); + engine.Snapshot.Storages.Delete(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type)); + } + + [ContractMethod(0_03000000, CallFlags.ReadStates)] + public string Resolve(StoreView snapshot, string name, RecordType type) + { + return Resolve(snapshot, name, type, 2); + } + + private string Resolve(StoreView snapshot, string name, RecordType type, int redirect) + { + if (redirect < 0) throw new InvalidOperationException(); + var dictionary = GetRecords(snapshot, name).ToDictionary(p => p.Type, p => p.Data); + if (dictionary.TryGetValue(type, out string data)) return data; + if (!dictionary.TryGetValue(RecordType.CNAME, out data)) return null; + return Resolve(snapshot, data, type, redirect - 1); + } + + public class NameState : NFTState + { + public uint Expiration; + public UInt160 Admin; + + public override byte[] Id => Utility.StrictUTF8.GetBytes(Name); + + public override JObject ToJson() + { + JObject json = base.ToJson(); + json["expiration"] = Expiration; + return json; + } + + public override void FromStackItem(StackItem stackItem) + { + base.FromStackItem(stackItem); + Struct @struct = (Struct)stackItem; + Expiration = (uint)@struct[3].GetInteger(); + Admin = @struct[4].IsNull ? null : new UInt160(@struct[4].GetSpan()); + } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + Struct @struct = (Struct)base.ToStackItem(referenceCounter); + @struct.Add(Expiration); + @struct.Add(Admin?.ToArray() ?? StackItem.Null); + return @struct; + } + } + + private class StringList : List, IInteroperable + { + void IInteroperable.FromStackItem(StackItem stackItem) + { + foreach (StackItem item in (VM.Types.Array)stackItem) + Add(item.GetString()); + } + + StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + { + return new VM.Types.Array(referenceCounter, this.Select(p => (ByteString)p)); + } + } + } +} diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 115a48a956..9220522ef1 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -25,6 +25,7 @@ public abstract class NativeContract public static PolicyContract Policy { get; } = new PolicyContract(); public static RoleManagement RoleManagement { get; } = new RoleManagement(); public static OracleContract Oracle { get; } = new OracleContract(); + public static NameService NameService { get; } = new NameService(); public string Name => GetType().Name; public NefFile Nef { get; } diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index 4ad9799269..b22ff83a54 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -24,7 +24,7 @@ public abstract class NonfungibleToken : NativeContract private const byte Prefix_TotalSupply = 11; private const byte Prefix_Account = 7; - private const byte Prefix_Token = 5; + protected const byte Prefix_Token = 5; protected NonfungibleToken() { @@ -75,17 +75,21 @@ protected void Mint(ApplicationEngine engine, TokenState token) protected void Burn(ApplicationEngine engine, byte[] tokenId) { - StorageKey key_token = CreateStorageKey(Prefix_Token).Add(GetKey(tokenId)); - TokenState token = engine.Snapshot.Storages.TryGet(key_token)?.GetInteroperable(); + Burn(engine, CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))); + } + + private protected void Burn(ApplicationEngine engine, StorageKey key) + { + TokenState token = engine.Snapshot.Storages.TryGet(key)?.GetInteroperable(); if (token is null) throw new InvalidOperationException(); - engine.Snapshot.Storages.Delete(key_token); + engine.Snapshot.Storages.Delete(key); StorageKey key_account = CreateStorageKey(Prefix_Account).Add(token.Owner); NFTAccountState account = engine.Snapshot.Storages.GetAndChange(key_account).GetInteroperable(); - account.Remove(tokenId); + account.Remove(token.Id); if (account.Balance.IsZero) engine.Snapshot.Storages.Delete(key_account); engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply)).Add(-1); - PostTransfer(engine, token.Owner, null, tokenId); + PostTransfer(engine, token.Owner, null, token.Id); } [ContractMethod(0_01000000, CallFlags.ReadStates)] diff --git a/src/neo/SmartContract/Native/RecordType.cs b/src/neo/SmartContract/Native/RecordType.cs new file mode 100644 index 0000000000..9ecf067c61 --- /dev/null +++ b/src/neo/SmartContract/Native/RecordType.cs @@ -0,0 +1,15 @@ +namespace Neo.SmartContract.Native +{ + public enum RecordType : byte + { + #region [RFC 1035](https://tools.ietf.org/html/rfc1035) + A = 1, + CNAME = 5, + TXT = 16, + #endregion + + #region [RFC 3596](https://tools.ietf.org/html/rfc3596) + AAAA = 28, + #endregion + } +} diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs new file mode 100644 index 0000000000..919d8edce9 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs @@ -0,0 +1,284 @@ +using Akka.TestKit.Xunit2; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.IO; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.UnitTests.Extensions; +using Neo.VM; +using System.Linq; +using System.Numerics; +using System.Text; + +namespace Neo.UnitTests.SmartContract.Native +{ + [TestClass] + public class UT_NameService : TestKit + { + protected StoreView _snapshot; + + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + _snapshot = Blockchain.Singleton.GetSnapshot(); + } + + [TestMethod] + public void TestInfo() + { + Assert.AreEqual("NameService", NativeContract.NameService.Name); + Assert.AreEqual("NNS", NativeContract.NameService.Symbol); + } + + [TestMethod] + public void TestRoots() + { + var snapshot = _snapshot.Clone(); + var persistingBlock = new Block() { Index = 1000 }; + var from = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // no match + var result = Check_AddRoot(snapshot, from, "te_st", persistingBlock); + Assert.IsFalse(result); + + // unsigned + result = Check_AddRoot(snapshot, UInt160.Zero, "test", persistingBlock); + Assert.IsFalse(result); + + // add root + result = Check_AddRoot(snapshot, from, "test", persistingBlock); + Assert.IsTrue(result); + CollectionAssert.AreEqual(new string[] { "test" }, NativeContract.NameService.GetRoots(snapshot).ToArray()); + + // add root twice + result = Check_AddRoot(snapshot, from, "test", persistingBlock); + Assert.IsFalse(result); + } + + [TestMethod] + public void TestPrice() + { + var snapshot = _snapshot.Clone(); + var persistingBlock = new Block() { Index = 1000 }; + var from = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // unsigned + var result = Check_SetPrice(snapshot, UInt160.Zero, 1, persistingBlock); + Assert.IsFalse(result); + + // under value + result = Check_SetPrice(snapshot, from, 0, persistingBlock); + Assert.IsFalse(result); + + // overvalue + result = Check_SetPrice(snapshot, from, 10000_00000001, persistingBlock); + Assert.IsFalse(result); + + // good + result = Check_SetPrice(snapshot, from, 55, persistingBlock); + Assert.IsTrue(result); + Assert.AreEqual(55, NativeContract.NameService.GetPrice(snapshot)); + } + + [TestMethod] + public void TestRegister() + { + var snapshot = _snapshot.Clone(); + var persistingBlock = new Block() { Index = 1000, Timestamp = 0 }; + var from = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // add root + var result = Check_AddRoot(snapshot, from, "com", persistingBlock); + Assert.IsTrue(result); + + // no-roots + result = Check_Register(snapshot, "neo.org", UInt160.Zero, persistingBlock); + Assert.IsFalse(result); + + // more than 2 dots + result = Check_Register(snapshot, "doc.neo.org", UInt160.Zero, persistingBlock); + Assert.IsFalse(result); + + // regex + Assert.IsFalse(Check_Register(snapshot, "\nneo.com", UInt160.Zero, persistingBlock)); + Assert.IsFalse(Check_Register(snapshot, "neo.com\n", UInt160.Zero, persistingBlock)); + + // good register + Assert.IsTrue(NativeContract.NameService.IsAvailable(snapshot, "neo.com")); + result = Check_Register(snapshot, "neo.com", UInt160.Zero, persistingBlock); + Assert.IsTrue(result); + Assert.AreEqual(31536000u, (uint)NativeContract.NameService.Properties(snapshot, Encoding.UTF8.GetBytes("neo.com"))["expiration"].AsNumber()); + Assert.IsFalse(NativeContract.NameService.IsAvailable(snapshot, "neo.com")); + + var resultInt = Check_Renew(snapshot, "neo.com", UInt160.Zero, persistingBlock); + Assert.AreEqual(31536000u * 2, (uint)resultInt); + Assert.AreEqual(31536000u * 2, (uint)NativeContract.NameService.Properties(snapshot, Encoding.UTF8.GetBytes("neo.com"))["expiration"].AsNumber()); + Assert.IsFalse(NativeContract.NameService.IsAvailable(snapshot, "neo.com")); + } + + [TestMethod] + public void TestSetRecord() + { + var snapshot = _snapshot.Clone(); + var persistingBlock = new Block() { Index = 1000, Timestamp = 0 }; + var from = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // add root + var result = Check_AddRoot(snapshot, from, "com", persistingBlock); + Assert.IsTrue(result); + + // good register + Assert.IsTrue(NativeContract.NameService.IsAvailable(snapshot, "neo.com")); + result = Check_Register(snapshot, "neo.com", UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), persistingBlock); + Assert.IsTrue(result); + result = Check_SetRecord(snapshot, "neo.com", RecordType.A, "8.8.8.8", UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), persistingBlock); + Assert.IsTrue(result); + Assert.AreEqual("8.8.8.8", NativeContract.NameService.GetRecord(snapshot, "neo.com", RecordType.A)); + CollectionAssert.AreEqual(new string[] { $"{RecordType.A}=8.8.8.8" }, NativeContract.NameService.GetRecords(snapshot, "neo.com").Select(u => u.Type.ToString() + "=" + u.Data).ToArray()); + + // wrong signed delete register + result = Check_DeleteRecord(snapshot, "neo.com", RecordType.A, UInt160.Zero, persistingBlock); + Assert.IsFalse(result); + + // set admin + result = Check_SetAdmin(snapshot, "neo.com", UInt160.Zero, UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), persistingBlock); + Assert.IsTrue(result); + + // delete register + result = Check_DeleteRecord(snapshot, "neo.com", RecordType.A, UInt160.Zero, persistingBlock); + Assert.IsTrue(result); + Assert.AreEqual(null, NativeContract.NameService.GetRecord(snapshot, "neo.com", RecordType.A)); + CollectionAssert.AreEqual(System.Array.Empty(), NativeContract.NameService.GetRecords(snapshot, "neo.com").Select(u => u.Type.ToString() + "=" + u.Data).ToArray()); + } + + internal static bool Check_DeleteRecord(StoreView snapshot, string name, RecordType type, UInt160 signedBy, Block persistingBlock) + { + var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "deleteRecord", false, new ContractParameter[] { + new ContractParameter(ContractParameterType.String) { Value = name }, + new ContractParameter(ContractParameterType.Integer) { Value = (int)type } + }); + engine.LoadScript(script.ToArray(), 0, -1, 0); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + return true; + } + + internal static bool Check_SetRecord(StoreView snapshot, string name, RecordType type, string data, UInt160 signedBy, Block persistingBlock) + { + var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "setRecord", false, new ContractParameter[] { + new ContractParameter(ContractParameterType.String) { Value = name }, + new ContractParameter(ContractParameterType.Integer) { Value = (int)type }, + new ContractParameter(ContractParameterType.String) { Value = data } + }); + engine.LoadScript(script.ToArray(), 0, -1, 0); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + return true; + } + + internal static BigInteger Check_Renew(StoreView snapshot, string name, UInt160 signedBy, Block persistingBlock) + { + var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "renew", true, new ContractParameter[] { + new ContractParameter(ContractParameterType.String) { Value = name } + }); + engine.LoadScript(script.ToArray(), 0, -1, 0); + + if (engine.Execute() == VMState.FAULT) + { + return -1; + } + + var result = engine.ResultStack.Pop(); + Assert.IsInstanceOfType(result, typeof(VM.Types.Integer)); + + return result.GetInteger(); + } + + internal static bool Check_SetAdmin(StoreView snapshot, string name, UInt160 admin, UInt160 signedBy, Block persistingBlock) + { + var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(admin, signedBy), snapshot, persistingBlock); + var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "setAdmin", false, new ContractParameter[] { + new ContractParameter(ContractParameterType.String) { Value = name }, + new ContractParameter(ContractParameterType.Hash160) { Value = admin } + }); + engine.LoadScript(script.ToArray(), 0, -1, 0); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + return true; + } + + internal static bool Check_Register(StoreView snapshot, string name, UInt160 owner, Block persistingBlock) + { + var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock); + var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "register", true, new ContractParameter[] { + new ContractParameter(ContractParameterType.String) { Value = name }, + new ContractParameter(ContractParameterType.Hash160) { Value = owner } + }); + engine.LoadScript(script.ToArray(), 0, -1, 0); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + var result = engine.ResultStack.Pop(); + Assert.IsInstanceOfType(result, typeof(VM.Types.Boolean)); + + return result.GetBoolean(); + } + + internal static bool Check_SetPrice(StoreView snapshot, UInt160 signedBy, long price, Block persistingBlock) + { + var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "setPrice", false, new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) { Value = price } }); + engine.LoadScript(script.ToArray(), 0, -1, 0); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + return true; + } + + internal static bool Check_AddRoot(StoreView snapshot, UInt160 signedBy, string root, Block persistingBlock) + { + var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "addRoot", false, new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = root } }); + engine.LoadScript(script.ToArray(), 0, -1, 0); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + return true; + } + } +} From d0a4976e9b06883a47426efc60a87c0d53fcd14f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 10 Jan 2021 19:25:24 +0800 Subject: [PATCH 24/65] Optimize dynamic call (#2211) --- .../Network/P2P/Payloads/OracleResponse.cs | 2 +- .../ApplicationEngine.Contract.cs | 19 +++++---- src/neo/SmartContract/ApplicationEngine.cs | 20 ++++++---- .../SmartContract/Native/NativeContract.cs | 4 +- src/neo/VM/Helper.cs | 17 ++++---- src/neo/Wallets/AssetDescriptor.cs | 4 +- src/neo/Wallets/Wallet.cs | 8 ++-- .../Extensions/NativeContractExtensions.cs | 6 +-- .../Network/P2P/Payloads/UT_Transaction.cs | 14 +++---- .../SmartContract/Native/UT_NameService.cs | 14 +++---- .../SmartContract/Native/UT_NativeContract.cs | 2 +- .../SmartContract/Native/UT_NeoToken.cs | 8 ++-- .../SmartContract/UT_InteropService.cs | 18 ++++----- .../SmartContract/UT_Syscalls.cs | 8 ++-- tests/neo.UnitTests/VM/UT_Helper.cs | 39 +++++++++---------- 15 files changed, 93 insertions(+), 90 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/OracleResponse.cs b/src/neo/Network/P2P/Payloads/OracleResponse.cs index 25f7797dbf..77ed8cf7ec 100644 --- a/src/neo/Network/P2P/Payloads/OracleResponse.cs +++ b/src/neo/Network/P2P/Payloads/OracleResponse.cs @@ -31,7 +31,7 @@ public class OracleResponse : TransactionAttribute static OracleResponse() { using ScriptBuilder sb = new ScriptBuilder(); - sb.EmitDynamicCall(NativeContract.Oracle.Hash, "finish", false); + sb.EmitDynamicCall(NativeContract.Oracle.Hash, "finish"); FixedScript = sb.ToArray(); } diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 17dd21fc4f..f40ada522c 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -1,8 +1,10 @@ using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; +using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; using Neo.VM.Types; using System; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract { @@ -20,17 +22,20 @@ partial class ApplicationEngine public static readonly InteropDescriptor System_Contract_NativeOnPersist = Register("System.Contract.NativeOnPersist", nameof(NativeOnPersist), 0, CallFlags.WriteStates); public static readonly InteropDescriptor System_Contract_NativePostPersist = Register("System.Contract.NativePostPersist", nameof(NativePostPersist), 0, CallFlags.WriteStates); - protected internal void CallContract(UInt160 contractHash, string method, CallFlags callFlags, bool hasReturnValue, ushort pcount) + protected internal void CallContract(UInt160 contractHash, string method, CallFlags callFlags, Array args) { if (method.StartsWith('_')) throw new ArgumentException($"Invalid Method Name: {method}"); if ((callFlags & ~CallFlags.All) != 0) throw new ArgumentOutOfRangeException(nameof(callFlags)); - if (pcount > CurrentContext.EvaluationStack.Count) - throw new InvalidOperationException(); - StackItem[] args = new StackItem[pcount]; - for (int i = 0; i < pcount; i++) - args[i] = Pop(); - CallContractInternal(contractHash, method, callFlags, hasReturnValue, args); + + ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, contractHash); + if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}"); + ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); + if (md is null) throw new InvalidOperationException($"Method {method} Does Not Exist In Contract {contractHash}"); + bool hasReturnValue = md.ReturnType != ContractParameterType.Void; + + if (!hasReturnValue) CurrentContext.EvaluationStack.Push(StackItem.Null); + CallContractInternal(contract, md, callFlags, hasReturnValue, args); } protected internal void CallNativeContract(string name) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 883ea6ef7e..f481e27726 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -82,16 +82,20 @@ private ExecutionContext CallContractInternal(UInt160 contractHash, string metho if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}"); ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); if (md is null) throw new InvalidOperationException($"Method {method} Does Not Exist In Contract {contractHash}"); + return CallContractInternal(contract, md, flags, hasReturnValue, args); + } - if (md.Safe) + private ExecutionContext CallContractInternal(ContractState contract, ContractMethodDescriptor method, CallFlags flags, bool hasReturnValue, IReadOnlyList args) + { + if (method.Safe) { flags &= ~CallFlags.WriteStates; } else { ContractState currentContract = NativeContract.ContractManagement.GetContract(Snapshot, CurrentScriptHash); - if (currentContract?.CanCall(contract, method) == false) - throw new InvalidOperationException($"Cannot Call Method {method} Of Contract {contractHash} From Contract {CurrentScriptHash}"); + if (currentContract?.CanCall(contract, method.Name) == false) + throw new InvalidOperationException($"Cannot Call Method {method} Of Contract {contract.Hash} From Contract {CurrentScriptHash}"); } if (invocationCounter.TryGetValue(contract.Hash, out var counter)) @@ -107,16 +111,16 @@ private ExecutionContext CallContractInternal(UInt160 contractHash, string metho UInt160 callingScriptHash = state.ScriptHash; CallFlags callingFlags = state.CallFlags; - if (args.Length != md.Parameters.Length) throw new InvalidOperationException($"Method {method} Expects {md.Parameters.Length} Arguments But Receives {args.Length} Arguments"); - if (hasReturnValue ^ (md.ReturnType != ContractParameterType.Void)) throw new InvalidOperationException("The return value type does not match."); - ExecutionContext context_new = LoadContract(contract, method, flags & callingFlags, hasReturnValue, (ushort)args.Length); + if (args.Count != method.Parameters.Length) throw new InvalidOperationException($"Method {method} Expects {method.Parameters.Length} Arguments But Receives {args.Count} Arguments"); + if (hasReturnValue ^ (method.ReturnType != ContractParameterType.Void)) throw new InvalidOperationException("The return value type does not match."); + ExecutionContext context_new = LoadContract(contract, method.Name, flags & callingFlags, hasReturnValue, (ushort)args.Count); state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; - for (int i = args.Length - 1; i >= 0; i--) + for (int i = args.Count - 1; i >= 0; i--) context_new.EvaluationStack.Push(args[i]); if (NativeContract.IsNative(contract.Hash)) - context_new.EvaluationStack.Push(method); + context_new.EvaluationStack.Push(method.Name); return context_new; } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 9220522ef1..ee4749bfc7 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -150,11 +150,11 @@ internal virtual void PostPersist(ApplicationEngine engine) { } - public ApplicationEngine TestCall(string operation, bool hasReturnValue, params object[] args) + public ApplicationEngine TestCall(string operation, params object[] args) { using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitDynamicCall(Hash, operation, hasReturnValue, args); + sb.EmitDynamicCall(Hash, operation, args); return ApplicationEngine.Run(sb.ToArray()); } } diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 764f7fd9ab..4eed45e1d2 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -46,10 +46,9 @@ public static ScriptBuilder Emit(this ScriptBuilder sb, params OpCode[] ops) return sb; } - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, bool hasReturnValue) + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation) { - sb.EmitPush(0); - sb.EmitPush(hasReturnValue ? 1 : 0); + sb.Emit(OpCode.NEWARRAY0); sb.EmitPush(CallFlags.All); sb.EmitPush(operation); sb.EmitPush(scriptHash); @@ -57,12 +56,12 @@ public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scrip return sb; } - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, bool hasReturnValue, params ContractParameter[] args) + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params ContractParameter[] args) { for (int i = args.Length - 1; i >= 0; i--) sb.EmitPush(args[i]); sb.EmitPush(args.Length); - sb.EmitPush(hasReturnValue ? 1 : 0); + sb.Emit(OpCode.PACK); sb.EmitPush(CallFlags.All); sb.EmitPush(operation); sb.EmitPush(scriptHash); @@ -70,12 +69,12 @@ public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scrip return sb; } - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, bool hasReturnValue, params object[] args) + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params object[] args) { for (int i = args.Length - 1; i >= 0; i--) sb.EmitPush(args[i]); sb.EmitPush(args.Length); - sb.EmitPush(hasReturnValue ? 1 : 0); + sb.Emit(OpCode.PACK); sb.EmitPush(CallFlags.All); sb.EmitPush(operation); sb.EmitPush(scriptHash); @@ -213,10 +212,10 @@ public static ScriptBuilder EmitSysCall(this ScriptBuilder sb, uint method, para /// contract operation /// operation arguments /// - public static byte[] MakeScript(this UInt160 scriptHash, string operation, bool hasReturnValue, params object[] args) + public static byte[] MakeScript(this UInt160 scriptHash, string operation, params object[] args) { using ScriptBuilder sb = new ScriptBuilder(); - sb.EmitDynamicCall(scriptHash, operation, hasReturnValue, args); + sb.EmitDynamicCall(scriptHash, operation, args); return sb.ToArray(); } diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index e13fb02589..3be315f86d 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -23,8 +23,8 @@ public AssetDescriptor(UInt160 asset_id) byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitDynamicCall(asset_id, "decimals", true); - sb.EmitDynamicCall(asset_id, "symbol", true); + sb.EmitDynamicCall(asset_id, "decimals"); + sb.EmitDynamicCall(asset_id, "symbol"); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, gas: 0_10000000); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 44cb5990f3..77015abf60 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -138,10 +138,10 @@ public BigDecimal GetBalance(UInt160 asset_id, params UInt160[] accounts) sb.EmitPush(0); foreach (UInt160 account in accounts) { - sb.EmitDynamicCall(asset_id, "balanceOf", true, account); + sb.EmitDynamicCall(asset_id, "balanceOf", account); sb.Emit(OpCode.ADD); } - sb.EmitDynamicCall(asset_id, "decimals", true); + sb.EmitDynamicCall(asset_id, "decimals"); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, gas: 20000000L * accounts.Length); @@ -265,7 +265,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null foreach (UInt160 account in accounts) using (ScriptBuilder sb2 = new ScriptBuilder()) { - sb2.EmitDynamicCall(assetId, "balanceOf", true, account); + sb2.EmitDynamicCall(assetId, "balanceOf", account); using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot)) { if (engine.State.HasFlag(VMState.FAULT)) @@ -296,7 +296,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null Scopes = WitnessScope.CalledByEntry }); } - sb.EmitDynamicCall(output.AssetId, "transfer", true, account, output.ScriptHash, value, output.Data); + sb.EmitDynamicCall(output.AssetId, "transfer", account, output.ScriptHash, value, output.Data); sb.Emit(OpCode.ASSERT); } } diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index 5a2d01fa30..a98b08b3b8 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -13,7 +13,7 @@ public static class NativeContractExtensions public static ContractState DeployContract(this StoreView snapshot, UInt160 sender, byte[] nefFile, byte[] manifest, long gas = 200_00000000) { var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", true, nefFile, manifest); + script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nefFile, manifest); var engine = ApplicationEngine.Create(TriggerType.Application, sender != null ? new Transaction() { Signers = new Signer[] { new Signer() { Account = sender } } } : null, snapshot, null, gas); @@ -34,7 +34,7 @@ public static ContractState DeployContract(this StoreView snapshot, UInt160 send public static void UpdateContract(this StoreView snapshot, UInt160 callingScriptHash, byte[] nefFile, byte[] manifest) { var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "update", false, nefFile, manifest); + script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "update", nefFile, manifest); var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(script.ToArray()); @@ -57,7 +57,7 @@ public static void UpdateContract(this StoreView snapshot, UInt160 callingScript public static void DestroyContract(this StoreView snapshot, UInt160 callingScriptHash) { var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "destroy", false); + script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "destroy"); var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(script.ToArray()); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 16aac9d21d..b4f0934307 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -319,7 +319,7 @@ public void FeeIsSignatureContract_TestScope_Global() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -405,7 +405,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -492,7 +492,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() { // self-transfer of 1e-8 GAS System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -580,7 +580,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() { // self-transfer of 1e-8 GAS System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -632,7 +632,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -722,7 +722,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -985,7 +985,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() { // self-transfer of 1e-8 GAS BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", true, acc.ScriptHash, acc.ScriptHash, value, null); + sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs index 919d8edce9..85a5daa48c 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs @@ -159,7 +159,7 @@ internal static bool Check_DeleteRecord(StoreView snapshot, string name, RecordT { var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.NameService.Hash, "deleteRecord", false, new ContractParameter[] { + script.EmitDynamicCall(NativeContract.NameService.Hash, "deleteRecord", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Integer) { Value = (int)type } }); @@ -177,7 +177,7 @@ internal static bool Check_SetRecord(StoreView snapshot, string name, RecordType { var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.NameService.Hash, "setRecord", false, new ContractParameter[] { + script.EmitDynamicCall(NativeContract.NameService.Hash, "setRecord", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Integer) { Value = (int)type }, new ContractParameter(ContractParameterType.String) { Value = data } @@ -196,7 +196,7 @@ internal static BigInteger Check_Renew(StoreView snapshot, string name, UInt160 { var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.NameService.Hash, "renew", true, new ContractParameter[] { + script.EmitDynamicCall(NativeContract.NameService.Hash, "renew", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name } }); engine.LoadScript(script.ToArray(), 0, -1, 0); @@ -216,7 +216,7 @@ internal static bool Check_SetAdmin(StoreView snapshot, string name, UInt160 adm { var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(admin, signedBy), snapshot, persistingBlock); var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.NameService.Hash, "setAdmin", false, new ContractParameter[] { + script.EmitDynamicCall(NativeContract.NameService.Hash, "setAdmin", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Hash160) { Value = admin } }); @@ -234,7 +234,7 @@ internal static bool Check_Register(StoreView snapshot, string name, UInt160 own { var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock); var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.NameService.Hash, "register", true, new ContractParameter[] { + script.EmitDynamicCall(NativeContract.NameService.Hash, "register", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Hash160) { Value = owner } }); @@ -255,7 +255,7 @@ internal static bool Check_SetPrice(StoreView snapshot, UInt160 signedBy, long p { var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.NameService.Hash, "setPrice", false, new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) { Value = price } }); + script.EmitDynamicCall(NativeContract.NameService.Hash, "setPrice", new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) { Value = price } }); engine.LoadScript(script.ToArray(), 0, -1, 0); if (engine.Execute() == VMState.FAULT) @@ -270,7 +270,7 @@ internal static bool Check_AddRoot(StoreView snapshot, UInt160 signedBy, string { var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.NameService.Hash, "addRoot", false, new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = root } }); + script.EmitDynamicCall(NativeContract.NameService.Hash, "addRoot", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = root } }); engine.LoadScript(script.ToArray(), 0, -1, 0); if (engine.Execute() == VMState.FAULT) diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index eed8e77026..9cd24fc66d 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -129,7 +129,7 @@ public void TestTrigger() [TestMethod] public void TestTestCall() { - ApplicationEngine engine = testNativeContract.TestCall("System.Blockchain.GetHeight", false, 0); + ApplicationEngine engine = testNativeContract.TestCall("System.Blockchain.GetHeight", 0); engine.ResultStack.Should().BeEmpty(); } } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 024641eff3..e580e6f465 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -511,7 +511,7 @@ public void TestCalculateBonus() [TestMethod] public void TestGetNextBlockValidators1() { - using (ApplicationEngine engine = NativeContract.NEO.TestCall("getNextBlockValidators", true)) + using (ApplicationEngine engine = NativeContract.NEO.TestCall("getNextBlockValidators")) { var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); @@ -544,7 +544,7 @@ public void TestGetNextBlockValidators2() [TestMethod] public void TestGetCandidates1() { - using ApplicationEngine engine = NativeContract.NEO.TestCall("getCandidates", true); + using ApplicationEngine engine = NativeContract.NEO.TestCall("getCandidates"); var array = engine.ResultStack.Pop(); array.Count.Should().Be(0); } @@ -605,7 +605,7 @@ public void TestCheckCandidate() [TestMethod] public void TestGetCommittee() { - using (ApplicationEngine engine = NativeContract.NEO.TestCall("getCommittee", true)) + using (ApplicationEngine engine = NativeContract.NEO.TestCall("getCommittee")) { var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); @@ -837,7 +837,7 @@ public void TestVote() })); } - sb.EmitDynamicCall(NativeContract.NEO.Hash, "transfer", true, from, UInt160.Zero, amount, null); + sb.EmitDynamicCall(NativeContract.NEO.Hash, "transfer", from, UInt160.Zero, amount, null); engine.LoadScript(sb.ToArray()); engine.Execute(); var result = engine.ResultStack.Peek(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 2c99d9b7a8..d5e70f1a23 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -89,7 +89,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitDynamicCall(scriptHash2, "test", true, "testEvent2", 1); + script.EmitDynamicCall(scriptHash2, "test", "testEvent2", 1); // Drop return @@ -141,7 +141,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitDynamicCall(scriptHash2, "test", true, "testEvent2", 1); + script.EmitDynamicCall(scriptHash2, "test", "testEvent2", 1); // Drop return @@ -224,7 +224,7 @@ public void TestExecutionEngine_GetCallingScriptHash() engine.Snapshot.AddContract(contract.Hash, contract); using ScriptBuilder scriptB = new ScriptBuilder(); - scriptB.EmitDynamicCall(contract.Hash, "test", true, 0, 1); + scriptB.EmitDynamicCall(contract.Hash, "test", 0, 1); engine.LoadScript(scriptB.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -596,21 +596,17 @@ public void TestContract_Call() var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); - engine.Push(args[1]); engine.Push(args[0]); - engine.CallContract(state.Hash, method, CallFlags.All, true, (ushort)args.Count); + engine.CallContract(state.Hash, method, CallFlags.All, args); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); state.Manifest.Permissions[0].Methods = WildcardContainer.Create("a"); - engine.Push(args[1]); engine.Push(args[0]); - Assert.ThrowsException(() => engine.CallContract(state.Hash, method, CallFlags.All, true, (ushort)args.Count)); + Assert.ThrowsException(() => engine.CallContract(state.Hash, method, CallFlags.All, args)); state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); - engine.Push(args[1]); engine.Push(args[0]); - engine.CallContract(state.Hash, method, CallFlags.All, true, (ushort)args.Count); + engine.CallContract(state.Hash, method, CallFlags.All, args); - engine.Push(args[1]); engine.Push(args[0]); - Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, method, CallFlags.All, true, (ushort)args.Count)); + Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, method, CallFlags.All, args)); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 029c04c623..ed7abb463e 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -349,10 +349,10 @@ public void System_Runtime_GetInvocationCounter() using (var script = new ScriptBuilder()) { - script.EmitDynamicCall(contractA.Hash, "dummyMain", true, 0, 1); - script.EmitDynamicCall(contractB.Hash, "dummyMain", true, 0, 1); - script.EmitDynamicCall(contractB.Hash, "dummyMain", true, 0, 1); - script.EmitDynamicCall(contractC.Hash, "dummyMain", true, 0, 1); + script.EmitDynamicCall(contractA.Hash, "dummyMain", 0, 1); + script.EmitDynamicCall(contractB.Hash, "dummyMain", 0, 1); + script.EmitDynamicCall(contractB.Hash, "dummyMain", 0, 1); + script.EmitDynamicCall(contractC.Hash, "dummyMain", 0, 1); // Execute diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 3b8333e531..896fa6f67d 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -51,20 +51,19 @@ public void TestToJson() public void TestEmitAppCall1() { ScriptBuilder sb = new ScriptBuilder(); - sb.EmitDynamicCall(UInt160.Zero, "AAAAA", false); - byte[] tempArray = new byte[37]; - tempArray[0] = (byte)OpCode.PUSH0; - tempArray[1] = (byte)OpCode.PUSH0; - tempArray[2] = (byte)OpCode.PUSH15;//(byte)CallFlags.All; - tempArray[3] = (byte)OpCode.PUSHDATA1; - tempArray[4] = 5;//operation.Length - Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 5, 5);//operation.data - tempArray[10] = (byte)OpCode.PUSHDATA1; - tempArray[11] = 0x14;//scriptHash.Length - Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 12, 20);//operation.data + sb.EmitDynamicCall(UInt160.Zero, "AAAAA"); + byte[] tempArray = new byte[36]; + tempArray[0] = (byte)OpCode.NEWARRAY0; + tempArray[1] = (byte)OpCode.PUSH15;//(byte)CallFlags.All; + tempArray[2] = (byte)OpCode.PUSHDATA1; + tempArray[3] = 5;//operation.Length + Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 4, 5);//operation.data + tempArray[9] = (byte)OpCode.PUSHDATA1; + tempArray[10] = 0x14;//scriptHash.Length + Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 11, 20);//operation.data uint api = ApplicationEngine.System_Contract_Call; - tempArray[32] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data + tempArray[31] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 32, 4);//api.data Assert.AreEqual(tempArray.ToHexString(), sb.ToArray().ToHexString()); } @@ -114,11 +113,11 @@ public void TestEmitMap() public void TestEmitAppCall2() { ScriptBuilder sb = new ScriptBuilder(); - sb.EmitDynamicCall(UInt160.Zero, "AAAAA", false, new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) }); + sb.EmitDynamicCall(UInt160.Zero, "AAAAA", new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) }); byte[] tempArray = new byte[38]; tempArray[0] = (byte)OpCode.PUSH0; tempArray[1] = (byte)OpCode.PUSH1; - tempArray[2] = (byte)OpCode.PUSH0; + tempArray[2] = (byte)OpCode.PACK; tempArray[3] = (byte)OpCode.PUSH15;//(byte)CallFlags.All; tempArray[4] = (byte)OpCode.PUSHDATA1; tempArray[5] = 0x05;//operation.Length @@ -136,11 +135,11 @@ public void TestEmitAppCall2() public void TestEmitAppCall3() { ScriptBuilder sb = new ScriptBuilder(); - sb.EmitDynamicCall(UInt160.Zero, "AAAAA", false, true); + sb.EmitDynamicCall(UInt160.Zero, "AAAAA", true); byte[] tempArray = new byte[38]; tempArray[0] = (byte)OpCode.PUSH1; - tempArray[1] = (byte)OpCode.PUSH1;//arg.Length - tempArray[2] = (byte)OpCode.PUSH0;//return + tempArray[1] = (byte)OpCode.PUSH1;//arg.Length + tempArray[2] = (byte)OpCode.PACK; tempArray[3] = (byte)OpCode.PUSH15;//(byte)CallFlags.All; tempArray[4] = (byte)OpCode.PUSHDATA1; tempArray[5] = 0x05;//operation.Length @@ -157,9 +156,9 @@ public void TestEmitAppCall3() [TestMethod] public void TestMakeScript() { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", true, UInt160.Zero); + byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - Assert.AreEqual("0c14000000000000000000000000000000000000000011111f0c0962616c616e63654f660c141717ddafdd757eec365865b963473beb617f9a1441627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c01f0c0962616c616e63654f660c141717ddafdd757eec365865b963473beb617f9a1441627d5b52", testScript.ToHexString()); } From dfb56a2b98db2ab4281067673526a490cd089b7a Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 10 Jan 2021 19:37:55 +0800 Subject: [PATCH 25/65] Automatically set Id for native contracts (#2210) --- .../ApplicationEngine.Contract.cs | 4 +-- .../Native/ContractManagement.cs | 2 -- src/neo/SmartContract/Native/GasToken.cs | 1 - src/neo/SmartContract/Native/NameService.cs | 1 - .../SmartContract/Native/NativeContract.cs | 18 +++++++---- src/neo/SmartContract/Native/NeoToken.cs | 1 - .../SmartContract/Native/OracleContract.cs | 2 -- .../SmartContract/Native/PolicyContract.cs | 2 -- .../SmartContract/Native/RoleManagement.cs | 2 -- .../SmartContract/Native/UT_FungibleToken.cs | 1 - .../SmartContract/Native/UT_NativeContract.cs | 6 +--- .../SmartContract/Native/UT_NeoToken.cs | 32 +++++++++---------- tests/neo.UnitTests/VM/UT_Helper.cs | 2 +- 13 files changed, 31 insertions(+), 43 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index f40ada522c..0d0312849d 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -38,9 +38,9 @@ protected internal void CallContract(UInt160 contractHash, string method, CallFl CallContractInternal(contract, md, callFlags, hasReturnValue, args); } - protected internal void CallNativeContract(string name) + protected internal void CallNativeContract(int id) { - NativeContract contract = NativeContract.GetContract(name); + NativeContract contract = NativeContract.GetContract(id); if (contract is null || contract.ActiveBlockIndex > Snapshot.Height) throw new InvalidOperationException(); contract.Invoke(this); diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index e161fa3ba3..a65d9fd100 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -14,8 +14,6 @@ namespace Neo.SmartContract.Native { public sealed class ContractManagement : NativeContract { - public override int Id => 0; - private const byte Prefix_MinimumDeploymentFee = 20; private const byte Prefix_NextAvailableId = 15; private const byte Prefix_Contract = 8; diff --git a/src/neo/SmartContract/Native/GasToken.cs b/src/neo/SmartContract/Native/GasToken.cs index db3e990c57..81e61b666c 100644 --- a/src/neo/SmartContract/Native/GasToken.cs +++ b/src/neo/SmartContract/Native/GasToken.cs @@ -6,7 +6,6 @@ namespace Neo.SmartContract.Native { public sealed class GasToken : FungibleToken { - public override int Id => -2; public override string Symbol => "GAS"; public override byte Decimals => 8; diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index d6be3d92c1..5defadeec3 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -19,7 +19,6 @@ namespace Neo.SmartContract.Native { public sealed class NameService : NonfungibleToken { - public override int Id => -6; public override string Symbol => "NNS"; private const byte Prefix_Roots = 10; diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index ee4749bfc7..fd47d5d6e2 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -14,11 +14,12 @@ namespace Neo.SmartContract.Native public abstract class NativeContract { private static readonly List contractsList = new List(); - private static readonly Dictionary contractsNameDictionary = new Dictionary(); + private static readonly Dictionary contractsIdDictionary = new Dictionary(); private static readonly Dictionary contractsHashDictionary = new Dictionary(); private readonly Dictionary methods = new Dictionary(); + private static int id_counter = 0; - public static IReadOnlyCollection Contracts { get; } = contractsList; + #region Named Native Contracts public static ContractManagement ContractManagement { get; } = new ContractManagement(); public static NeoToken NEO { get; } = new NeoToken(); public static GasToken GAS { get; } = new GasToken(); @@ -26,21 +27,24 @@ public abstract class NativeContract public static RoleManagement RoleManagement { get; } = new RoleManagement(); public static OracleContract Oracle { get; } = new OracleContract(); public static NameService NameService { get; } = new NameService(); + #endregion + public static IReadOnlyCollection Contracts { get; } = contractsList; public string Name => GetType().Name; public NefFile Nef { get; } public byte[] Script => Nef.Script; public UInt160 Hash { get; } - public abstract int Id { get; } + public int Id { get; } public ContractManifest Manifest { get; } public uint ActiveBlockIndex { get; } protected NativeContract() { + this.Id = --id_counter; byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitPush(Name); + sb.EmitPush(Id); sb.EmitSysCall(ApplicationEngine.System_Contract_CallNative); script = sb.ToArray(); } @@ -85,7 +89,7 @@ protected NativeContract() if (ProtocolSettings.Default.NativeActivations.TryGetValue(Name, out uint activationIndex)) this.ActiveBlockIndex = activationIndex; contractsList.Add(this); - contractsNameDictionary.Add(Name, this); + contractsIdDictionary.Add(Id, this); contractsHashDictionary.Add(Hash, this); } @@ -106,9 +110,9 @@ public static NativeContract GetContract(UInt160 hash) return contract; } - public static NativeContract GetContract(string name) + public static NativeContract GetContract(int id) { - contractsNameDictionary.TryGetValue(name, out var contract); + contractsIdDictionary.TryGetValue(id, out var contract); return contract; } diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index e9c80ed43d..375cb628ef 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -17,7 +17,6 @@ namespace Neo.SmartContract.Native { public sealed class NeoToken : FungibleToken { - public override int Id => -1; public override string Symbol => "NEO"; public override byte Decimals => 0; public BigInteger TotalAmount { get; } diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index 92e00c2565..b660f347ff 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -28,8 +28,6 @@ public sealed class OracleContract : NativeContract private const long OracleRequestPrice = 0_50000000; - public override int Id => -5; - internal OracleContract() { var events = new List(Manifest.Abi.Events) diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index ccec40ea2a..c873f4ce14 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -11,8 +11,6 @@ namespace Neo.SmartContract.Native { public sealed class PolicyContract : NativeContract { - public override int Id => -3; - public const uint DefaultExecFeeFactor = 30; public const uint DefaultStoragePrice = 100000; private const uint MaxExecFeeFactor = 1000; diff --git a/src/neo/SmartContract/Native/RoleManagement.cs b/src/neo/SmartContract/Native/RoleManagement.cs index 6c32ca7c4b..ee95bd3edb 100644 --- a/src/neo/SmartContract/Native/RoleManagement.cs +++ b/src/neo/SmartContract/Native/RoleManagement.cs @@ -14,8 +14,6 @@ namespace Neo.SmartContract.Native { public sealed class RoleManagement : NativeContract { - public override int Id => -4; - internal RoleManagement() { } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs index 8b57384f5e..8018dcaa16 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs @@ -79,7 +79,6 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null) public class TestNep17Token : FungibleToken { - public override int Id => 0x10000005; public override string Symbol => throw new NotImplementedException(); public override byte Decimals => 8; } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 9cd24fc66d..601c21ab52 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -32,8 +32,6 @@ public void TestInitialize() private class DummyNative : NativeContract { - public override int Id => 1; - [ContractMethod(0, CallFlags.None)] public void NetTypes( bool p1, sbyte p2, byte p3, short p4, ushort p5, int p6, uint p7, long p8, ulong p9, BigInteger p10, @@ -90,7 +88,7 @@ public void TestToParameter() [TestMethod] public void TestGetContract() { - Assert.IsTrue(NativeContract.NEO == NativeContract.GetContract(NativeContract.NEO.Name)); + Assert.IsTrue(NativeContract.NEO == NativeContract.GetContract(NativeContract.NEO.Id)); Assert.IsTrue(NativeContract.NEO == NativeContract.GetContract(NativeContract.NEO.Hash)); } @@ -136,8 +134,6 @@ public void TestTestCall() public class TestNativeContract : NativeContract { - public override int Id => 0x10000006; - [ContractMethod(0, CallFlags.None)] public string HelloWorld => "hello world"; diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index e580e6f465..133df46bd3 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -503,7 +503,7 @@ public void TestCalculateBonus() Balance = 100, VoteTo = Blockchain.StandbyCommittee[0] })); - snapshot.Storages.Add(new KeyBuilder(-1, 23).Add(Blockchain.StandbyCommittee[0]).AddBigEndian(uint.MaxValue - 50), new StorageItem() { Value = new BigInteger(50 * 10000L).ToByteArray() }); + snapshot.Storages.Add(new KeyBuilder(-2, 23).Add(Blockchain.StandbyCommittee[0]).AddBigEndian(uint.MaxValue - 50), new StorageItem() { Value = new BigInteger(50 * 10000L).ToByteArray() }); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 100).Should().Be(new BigInteger(50 * 100)); snapshot.Storages.Delete(key); } @@ -569,22 +569,22 @@ public void TestCheckCandidate() var point = committee[0].EncodePoint(true); // Prepare Prefix_VoterRewardPerCommittee - var storageKey = new KeyBuilder(-1, 23).Add(committee[0]).AddBigEndian(20); + var storageKey = new KeyBuilder(-2, 23).Add(committee[0]).AddBigEndian(20); snapshot.Storages.Add(storageKey, new StorageItem(new BigInteger(1000))); // Prepare Candidate - storageKey = new KeyBuilder(-1, 33).Add(committee[0]); + storageKey = new KeyBuilder(-2, 33).Add(committee[0]); snapshot.Storages.Add(storageKey, new StorageItem(new CandidateState { Registered = true, Votes = BigInteger.One })); - storageKey = new KeyBuilder(-1, 23).Add(committee[0]); + storageKey = new KeyBuilder(-2, 23).Add(committee[0]); snapshot.Storages.Find(storageKey.ToArray()).ToArray().Length.Should().Be(1); // Pre-persist - var persistingBlock = new Block { Index = 21, Transactions = Array.Empty() }; + var persistingBlock = new Block { Index = 21, ConsensusData = new ConsensusData(), Transactions = Array.Empty() }; Check_OnPersist(snapshot, persistingBlock); // Clear votes - storageKey = new KeyBuilder(-1, 33).Add(committee[0]); + storageKey = new KeyBuilder(-2, 33).Add(committee[0]); snapshot.Storages.GetAndChange(storageKey).GetInteroperable().Votes = BigInteger.Zero; // Unregister candidate, remove @@ -592,13 +592,13 @@ public void TestCheckCandidate() ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); - storageKey = new KeyBuilder(-1, 23).Add(committee[0]); + storageKey = new KeyBuilder(-2, 23).Add(committee[0]); snapshot.Storages.Find(storageKey.ToArray()).ToArray().Length.Should().Be(0); // Post-persist Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); - storageKey = new KeyBuilder(-1, 23).Add(committee[0]); + storageKey = new KeyBuilder(-2, 23).Add(committee[0]); snapshot.Storages.Find(storageKey.ToArray()).ToArray().Length.Should().Be(1); } @@ -713,16 +713,16 @@ public void TestClaimGas() for (var i = 0; i < ProtocolSettings.Default.CommitteeMembersCount; i++) { ECPoint member = standbyCommittee[i]; - snapshot.Storages.Add(new KeyBuilder(-1, 33).Add(member), new StorageItem(new CandidateState() + snapshot.Storages.Add(new KeyBuilder(-2, 33).Add(member), new StorageItem(new CandidateState() { Registered = true, Votes = 200 * 10000 })); cachedCommittee.Add((member, 200 * 10000)); } - snapshot.Storages[new KeyBuilder(-1, 14)].Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), 4096); + snapshot.Storages[new KeyBuilder(-2, 14)].Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), 4096); - var item = snapshot.Storages.GetAndChange(new KeyBuilder(-1, 1), () => new StorageItem()); + var item = snapshot.Storages.GetAndChange(new KeyBuilder(-2, 1), () => new StorageItem()); item.Value = ((BigInteger)2100 * 10000L).ToByteArray(); var persistingBlock = new Block { Index = 0, Transactions = Array.Empty() }; @@ -733,10 +733,10 @@ public void TestClaimGas() var accountB = committee[ProtocolSettings.Default.CommitteeMembersCount - 1]; NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(accountA).ScriptHash).Should().Be(0); - StorageItem storageItem = snapshot.Storages.TryGet(new KeyBuilder(-1, 23).Add(accountA).AddBigEndian(1)); + StorageItem storageItem = snapshot.Storages.TryGet(new KeyBuilder(-2, 23).Add(accountA).AddBigEndian(1)); new BigInteger(storageItem.Value).Should().Be(30000000000); - snapshot.Storages.TryGet(new KeyBuilder(-1, 23).Add(accountB).AddBigEndian(uint.MaxValue - 1)).Should().BeNull(); + snapshot.Storages.TryGet(new KeyBuilder(-2, 23).Add(accountB).AddBigEndian(uint.MaxValue - 1)).Should().BeNull(); // Next block @@ -745,7 +745,7 @@ public void TestClaimGas() NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[1]).ScriptHash).Should().Be(0); - storageItem = snapshot.Storages.TryGet(new KeyBuilder(-1, 23).Add(committee[1]).AddBigEndian(1)); + storageItem = snapshot.Storages.TryGet(new KeyBuilder(-2, 23).Add(committee[1]).AddBigEndian(1)); new BigInteger(storageItem.Value).Should().Be(30000000000); // Next block @@ -756,13 +756,13 @@ public void TestClaimGas() accountA = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray()[2]; NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[2]).ScriptHash).Should().Be(0); - storageItem = snapshot.Storages.TryGet(new KeyBuilder(-1, 23).Add(committee[2]).AddBigEndian(22)); + storageItem = snapshot.Storages.TryGet(new KeyBuilder(-2, 23).Add(committee[2]).AddBigEndian(22)); new BigInteger(storageItem.Value).Should().Be(30000000000 * 2); // Claim GAS var account = Contract.CreateSignatureContract(committee[2]).ScriptHash; - snapshot.Storages.Add(new KeyBuilder(-1, 20).Add(account), new StorageItem(new NeoAccountState + snapshot.Storages.Add(new KeyBuilder(-2, 20).Add(account), new StorageItem(new NeoAccountState { BalanceHeight = 3, Balance = 200 * 10000 - 2 * 100, diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 896fa6f67d..4135a53f84 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -158,7 +158,7 @@ public void TestMakeScript() { byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - Assert.AreEqual("0c14000000000000000000000000000000000000000011c01f0c0962616c616e63654f660c141717ddafdd757eec365865b963473beb617f9a1441627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c01f0c0962616c616e63654f660c14a282435d0ee334fecaac7f5fde46f623f24cc09a41627d5b52", testScript.ToHexString()); } From a9fe75dc262dffe9af68c987d5a4830d3fe51fdb Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 10 Jan 2021 19:40:26 +0800 Subject: [PATCH 26/65] Use ExtensiblePayload in consensus (#2202) --- src/neo/Consensus/ConsensusContext.cs | 147 +++++++++---- src/neo/Consensus/ConsensusMessage.cs | 14 +- src/neo/Consensus/ConsensusService.cs | 121 ++++++----- src/neo/Consensus/PrepareRequest.cs | 10 +- ...ecoveryMessage.ChangeViewPayloadCompact.cs | 13 -- .../RecoveryMessage.CommitPayloadCompact.cs | 13 -- ...coveryMessage.PreparationPayloadCompact.cs | 10 - src/neo/Consensus/RecoveryMessage.cs | 86 ++------ src/neo/Ledger/Blockchain.cs | 2 +- src/neo/Network/P2P/Message.cs | 1 - src/neo/Network/P2P/MessageCommand.cs | 2 - .../Network/P2P/Payloads/ConsensusPayload.cs | 130 ------------ .../Network/P2P/Payloads/ExtensiblePayload.cs | 2 +- src/neo/Network/P2P/Payloads/InventoryType.cs | 3 +- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 1 - src/neo/Network/P2P/RemoteNode.cs | 2 - src/neo/Network/P2P/TaskManager.cs | 2 +- src/neo/Plugins/IP2PPlugin.cs | 1 - .../Consensus/UT_ChangeViewPayloadCompact.cs | 13 -- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 195 +++++++++++------- .../Consensus/UT_ConsensusContext.cs | 1 - .../Consensus/UT_ConsensusServiceMailbox.cs | 2 +- .../Consensus/UT_RecoveryRequest.cs | 2 +- .../Network/P2P/UT_RemoteNodeMailbox.cs | 4 +- .../Network/P2P/UT_TaskManagerMailbox.cs | 2 +- tests/neo.UnitTests/Plugins/UT_Plugin.cs | 2 - tests/neo.UnitTests/TestBlockchain.cs | 18 +- 27 files changed, 359 insertions(+), 440 deletions(-) delete mode 100644 src/neo/Network/P2P/Payloads/ConsensusPayload.cs diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index b1ac6b33f5..3101c2d54d 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -13,6 +13,7 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using static Neo.Consensus.RecoveryMessage; namespace Neo.Consensus { @@ -29,10 +30,10 @@ public class ConsensusContext : IDisposable, ISerializable public int MyIndex; public UInt256[] TransactionHashes; public Dictionary Transactions; - public ConsensusPayload[] PreparationPayloads; - public ConsensusPayload[] CommitPayloads; - public ConsensusPayload[] ChangeViewPayloads; - public ConsensusPayload[] LastChangeViewPayloads; + public ExtensiblePayload[] PreparationPayloads; + public ExtensiblePayload[] CommitPayloads; + public ExtensiblePayload[] ChangeViewPayloads; + public ExtensiblePayload[] LastChangeViewPayloads; // LastSeenMessage array stores the height of the last seen message, for each validator. // if this node never heard from validator i, LastSeenMessage[i] will be -1. public Dictionary LastSeenMessage { get; private set; } @@ -47,6 +48,7 @@ public class ConsensusContext : IDisposable, ISerializable private int _witnessSize; private readonly Wallet wallet; private readonly IStore store; + private Dictionary cachedMessages; public int F => (Validators.Length - 1) / 3; public int M => Validators.Length - F; @@ -79,7 +81,7 @@ public bool ValidatorsChanged public bool ResponseSent => !WatchOnly && PreparationPayloads[MyIndex] != null; public bool CommitSent => !WatchOnly && CommitPayloads[MyIndex] != null; public bool BlockSent => Block.Transactions != null; - public bool ViewChanging => !WatchOnly && ChangeViewPayloads[MyIndex]?.GetDeserializedMessage().NewViewNumber > ViewNumber; + public bool ViewChanging => !WatchOnly && GetMessage(ChangeViewPayloads[MyIndex])?.NewViewNumber > ViewNumber; public bool NotAcceptingPayloadsDueToViewChanging => ViewChanging && !MoreThanFNodesCommittedOrLost; // A possible attack can happen if the last node to commit is malicious and either sends change view after his // commit to stall nodes in a higher view, or if he refuses to send recovery messages. In addition, if a node @@ -104,8 +106,8 @@ public Block CreateBlock() ContractParametersContext sc = new ContractParametersContext(Block); for (int i = 0, j = 0; i < Validators.Length && j < M; i++) { - if (CommitPayloads[i]?.ConsensusMessage.ViewNumber != ViewNumber) continue; - sc.AddSignature(contract, Validators[i], CommitPayloads[i].GetDeserializedMessage().Signature); + if (GetMessage(CommitPayloads[i])?.ViewNumber != ViewNumber) continue; + sc.AddSignature(contract, Validators[i], GetMessage(CommitPayloads[i]).Signature); j++; } Block.Witness = sc.GetWitnesses()[0]; @@ -113,6 +115,25 @@ public Block CreateBlock() return Block; } + public ExtensiblePayload CreatePayload(ConsensusMessage message, byte[] invocationScript = null) + { + ExtensiblePayload payload = new ExtensiblePayload + { + Category = "Consensus", + ValidBlockStart = 0, + ValidBlockEnd = message.BlockIndex, + Sender = GetSender(message.ValidatorIndex), + Data = message.ToArray(), + Witness = invocationScript is null ? null : new Witness + { + InvocationScript = invocationScript, + VerificationScript = Contract.CreateSignatureRedeemScript(Validators[message.ValidatorIndex]) + } + }; + cachedMessages.TryAdd(payload.Hash, message); + return payload; + } + public void Deserialize(BinaryReader reader) { Reset(0); @@ -126,10 +147,10 @@ public void Deserialize(BinaryReader reader) ViewNumber = reader.ReadByte(); TransactionHashes = reader.ReadSerializableArray(); Transaction[] transactions = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - PreparationPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - CommitPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - ChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - LastChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); + PreparationPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); + CommitPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); + ChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); + LastChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); if (TransactionHashes.Length == 0 && !RequestSentOrReceived) TransactionHashes = null; Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); @@ -154,6 +175,52 @@ public Block EnsureHeader() return Block; } + public ConsensusMessage GetMessage(ExtensiblePayload payload) + { + if (payload is null) return null; + if (!cachedMessages.TryGetValue(payload.Hash, out ConsensusMessage message)) + cachedMessages.Add(payload.Hash, message = ConsensusMessage.DeserializeFrom(payload.Data)); + return message; + } + + public T GetMessage(ExtensiblePayload payload) where T : ConsensusMessage + { + return (T)GetMessage(payload); + } + + private ChangeViewPayloadCompact GetChangeViewPayloadCompact(ExtensiblePayload payload) + { + ChangeView message = GetMessage(payload); + return new ChangeViewPayloadCompact + { + ValidatorIndex = message.ValidatorIndex, + OriginalViewNumber = message.ViewNumber, + Timestamp = message.Timestamp, + InvocationScript = payload.Witness.InvocationScript + }; + } + + private CommitPayloadCompact GetCommitPayloadCompact(ExtensiblePayload payload) + { + Commit message = GetMessage(payload); + return new CommitPayloadCompact + { + ViewNumber = message.ViewNumber, + ValidatorIndex = message.ValidatorIndex, + Signature = message.Signature, + InvocationScript = payload.Witness.InvocationScript + }; + } + + private PreparationPayloadCompact GetPreparationPayloadCompact(ExtensiblePayload payload) + { + return new PreparationPayloadCompact + { + ValidatorIndex = GetMessage(payload).ValidatorIndex, + InvocationScript = payload.Witness.InvocationScript + }; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte GetPrimaryIndex(byte viewNumber) { @@ -161,6 +228,11 @@ public byte GetPrimaryIndex(byte viewNumber) return p >= 0 ? (byte)p : (byte)(p + Validators.Length); } + public UInt160 GetSender(int index) + { + return Contract.CreateSignatureRedeemScript(Validators[index]).ToScriptHash(); + } + public bool Load() { byte[] data = store.TryGet(ConsensusStatePrefix, null); @@ -180,7 +252,7 @@ public bool Load() } } - public ConsensusPayload MakeChangeView(ChangeViewReason reason) + public ExtensiblePayload MakeChangeView(ChangeViewReason reason) { return ChangeViewPayloads[MyIndex] = MakeSignedPayload(new ChangeView { @@ -189,7 +261,7 @@ public ConsensusPayload MakeChangeView(ChangeViewReason reason) }); } - public ConsensusPayload MakeCommit() + public ExtensiblePayload MakeCommit() { return CommitPayloads[MyIndex] ?? (CommitPayloads[MyIndex] = MakeSignedPayload(new Commit { @@ -197,22 +269,17 @@ public ConsensusPayload MakeCommit() })); } - private ConsensusPayload MakeSignedPayload(ConsensusMessage message) + private ExtensiblePayload MakeSignedPayload(ConsensusMessage message) { + message.BlockIndex = Block.Index; + message.ValidatorIndex = (byte)MyIndex; message.ViewNumber = ViewNumber; - ConsensusPayload payload = new ConsensusPayload - { - Version = Block.Version, - PrevHash = Block.PrevHash, - BlockIndex = Block.Index, - ValidatorIndex = (byte)MyIndex, - ConsensusMessage = message - }; + ExtensiblePayload payload = CreatePayload(message, null); SignPayload(payload); return payload; } - private void SignPayload(ConsensusPayload payload) + private void SignPayload(ExtensiblePayload payload) { ContractParametersContext sc; try @@ -308,7 +375,7 @@ internal void EnsureMaxBlockLimitation(IEnumerable txs) TransactionHashes = hashes.ToArray(); } - public ConsensusPayload MakePrepareRequest() + public ExtensiblePayload MakePrepareRequest() { var random = new Random(); Span buffer = stackalloc byte[sizeof(ulong)]; @@ -319,13 +386,15 @@ public ConsensusPayload MakePrepareRequest() return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareRequest { + Version = Block.Version, + PrevHash = Block.PrevHash, Timestamp = Block.Timestamp, Nonce = Block.ConsensusData.Nonce, TransactionHashes = TransactionHashes }); } - public ConsensusPayload MakeRecoveryRequest() + public ExtensiblePayload MakeRecoveryRequest() { return MakeSignedPayload(new RecoveryRequest { @@ -333,33 +402,36 @@ public ConsensusPayload MakeRecoveryRequest() }); } - public ConsensusPayload MakeRecoveryMessage() + public ExtensiblePayload MakeRecoveryMessage() { PrepareRequest prepareRequestMessage = null; if (TransactionHashes != null) { prepareRequestMessage = new PrepareRequest { + Version = Block.Version, + PrevHash = Block.PrevHash, ViewNumber = ViewNumber, Timestamp = Block.Timestamp, + BlockIndex = Block.Index, Nonce = Block.ConsensusData.Nonce, TransactionHashes = TransactionHashes }; } return MakeSignedPayload(new RecoveryMessage() { - ChangeViewMessages = LastChangeViewPayloads.Where(p => p != null).Select(p => RecoveryMessage.ChangeViewPayloadCompact.FromPayload(p)).Take(M).ToDictionary(p => (int)p.ValidatorIndex), + ChangeViewMessages = LastChangeViewPayloads.Where(p => p != null).Select(p => GetChangeViewPayloadCompact(p)).Take(M).ToDictionary(p => (int)p.ValidatorIndex), PrepareRequestMessage = prepareRequestMessage, // We only need a PreparationHash set if we don't have the PrepareRequest information. - PreparationHash = TransactionHashes == null ? PreparationPayloads.Where(p => p != null).GroupBy(p => p.GetDeserializedMessage().PreparationHash, (k, g) => new { Hash = k, Count = g.Count() }).OrderByDescending(p => p.Count).Select(p => p.Hash).FirstOrDefault() : null, - PreparationMessages = PreparationPayloads.Where(p => p != null).Select(p => RecoveryMessage.PreparationPayloadCompact.FromPayload(p)).ToDictionary(p => (int)p.ValidatorIndex), + PreparationHash = TransactionHashes == null ? PreparationPayloads.Where(p => p != null).GroupBy(p => GetMessage(p).PreparationHash, (k, g) => new { Hash = k, Count = g.Count() }).OrderByDescending(p => p.Count).Select(p => p.Hash).FirstOrDefault() : null, + PreparationMessages = PreparationPayloads.Where(p => p != null).Select(p => GetPreparationPayloadCompact(p)).ToDictionary(p => (int)p.ValidatorIndex), CommitMessages = CommitSent - ? CommitPayloads.Where(p => p != null).Select(p => RecoveryMessage.CommitPayloadCompact.FromPayload(p)).ToDictionary(p => (int)p.ValidatorIndex) - : new Dictionary() + ? CommitPayloads.Where(p => p != null).Select(p => GetCommitPayloadCompact(p)).ToDictionary(p => (int)p.ValidatorIndex) + : new Dictionary() }); } - public ConsensusPayload MakePrepareResponse() + public ExtensiblePayload MakePrepareResponse() { return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareResponse { @@ -401,9 +473,9 @@ public void Reset(byte viewNumber) } } MyIndex = -1; - ChangeViewPayloads = new ConsensusPayload[Validators.Length]; - LastChangeViewPayloads = new ConsensusPayload[Validators.Length]; - CommitPayloads = new ConsensusPayload[Validators.Length]; + ChangeViewPayloads = new ExtensiblePayload[Validators.Length]; + LastChangeViewPayloads = new ExtensiblePayload[Validators.Length]; + CommitPayloads = new ExtensiblePayload[Validators.Length]; if (ValidatorsChanged || LastSeenMessage is null) { var previous_last_seen_message = LastSeenMessage; @@ -425,11 +497,12 @@ public void Reset(byte viewNumber) keyPair = account.GetKey(); break; } + cachedMessages = new Dictionary(); } else { for (int i = 0; i < LastChangeViewPayloads.Length; i++) - if (ChangeViewPayloads[i]?.GetDeserializedMessage().NewViewNumber >= viewNumber) + if (GetMessage(ChangeViewPayloads[i])?.NewViewNumber >= viewNumber) LastChangeViewPayloads[i] = ChangeViewPayloads[i]; else LastChangeViewPayloads[i] = null; @@ -443,7 +516,7 @@ public void Reset(byte viewNumber) Block.Timestamp = 0; Block.Transactions = null; TransactionHashes = null; - PreparationPayloads = new ConsensusPayload[Validators.Length]; + PreparationPayloads = new ExtensiblePayload[Validators.Length]; if (MyIndex >= 0) LastSeenMessage[Validators[MyIndex]] = Block.Index; } diff --git a/src/neo/Consensus/ConsensusMessage.cs b/src/neo/Consensus/ConsensusMessage.cs index 856ccdc72b..fceae7073a 100644 --- a/src/neo/Consensus/ConsensusMessage.cs +++ b/src/neo/Consensus/ConsensusMessage.cs @@ -8,9 +8,15 @@ namespace Neo.Consensus public abstract class ConsensusMessage : ISerializable { public readonly ConsensusMessageType Type; + public uint BlockIndex; + public byte ValidatorIndex; public byte ViewNumber; - public virtual int Size => sizeof(ConsensusMessageType) + sizeof(byte); + public virtual int Size => + sizeof(ConsensusMessageType) + //Type + sizeof(uint) + //BlockIndex + sizeof(byte) + //ValidatorIndex + sizeof(byte); //ViewNumber protected ConsensusMessage(ConsensusMessageType type) { @@ -23,6 +29,10 @@ public virtual void Deserialize(BinaryReader reader) { if (Type != (ConsensusMessageType)reader.ReadByte()) throw new FormatException(); + BlockIndex = reader.ReadUInt32(); + ValidatorIndex = reader.ReadByte(); + if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount) + throw new FormatException(); ViewNumber = reader.ReadByte(); } @@ -37,6 +47,8 @@ public static ConsensusMessage DeserializeFrom(byte[] data) public virtual void Serialize(BinaryWriter writer) { writer.Write((byte)Type); + writer.Write(BlockIndex); + writer.Write(ValidatorIndex); writer.Write(ViewNumber); } } diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 3f9f3d29b6..3361315e06 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -7,7 +7,7 @@ using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Plugins; +using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.Wallets; using System; @@ -134,7 +134,7 @@ private void ChangeTimer(TimeSpan delay) private void CheckCommits() { - if (context.CommitPayloads.Count(p => p?.ConsensusMessage.ViewNumber == context.ViewNumber) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) + if (context.CommitPayloads.Count(p => context.GetMessage(p)?.ViewNumber == context.ViewNumber) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) { block_received_index = context.Block.Index; block_received_time = TimeProvider.Current.UtcNow; @@ -147,12 +147,13 @@ private void CheckCommits() private void CheckExpectedView(byte viewNumber) { if (context.ViewNumber >= viewNumber) return; + var messages = context.ChangeViewPayloads.Select(p => context.GetMessage(p)).ToArray(); // if there are `M` change view payloads with NewViewNumber greater than viewNumber, then, it is safe to move - if (context.ChangeViewPayloads.Count(p => p != null && p.GetDeserializedMessage().NewViewNumber >= viewNumber) >= context.M) + if (messages.Count(p => p != null && p.NewViewNumber >= viewNumber) >= context.M) { if (!context.WatchOnly) { - ChangeView message = context.ChangeViewPayloads[context.MyIndex]?.GetDeserializedMessage(); + ChangeView message = messages[context.MyIndex]; // Communicate the network about my agreement to move to `viewNumber` // if my last change view payload, `message`, has NewViewNumber lower than current view to change if (message is null || message.NewViewNumber < viewNumber) @@ -166,7 +167,7 @@ private void CheckPreparations() { if (context.PreparationPayloads.Count(p => p != null) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) { - ConsensusPayload payload = context.MakeCommit(); + ExtensiblePayload payload = context.MakeCommit(); Log($"send commit"); context.Save(); localNode.Tell(new LocalNode.SendDirectly { Inventory = payload }); @@ -214,29 +215,29 @@ private void Log(string message, LogLevel level = LogLevel.Info) Utility.Log(nameof(ConsensusService), level, message); } - private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) + private void OnChangeViewReceived(ExtensiblePayload payload, ChangeView message) { if (message.NewViewNumber <= context.ViewNumber) - OnRecoveryRequestReceived(payload); + OnRecoveryRequestReceived(payload, message); if (context.CommitSent) return; - var expectedView = context.ChangeViewPayloads[payload.ValidatorIndex]?.GetDeserializedMessage().NewViewNumber ?? (byte)0; + var expectedView = context.GetMessage(context.ChangeViewPayloads[message.ValidatorIndex])?.NewViewNumber ?? 0; if (message.NewViewNumber <= expectedView) return; - Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber} reason={message.Reason}"); - context.ChangeViewPayloads[payload.ValidatorIndex] = payload; + Log($"{nameof(OnChangeViewReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex} nv={message.NewViewNumber} reason={message.Reason}"); + context.ChangeViewPayloads[message.ValidatorIndex] = payload; CheckExpectedView(message.NewViewNumber); } - private void OnCommitReceived(ConsensusPayload payload, Commit commit) + private void OnCommitReceived(ExtensiblePayload payload, Commit commit) { - ref ConsensusPayload existingCommitPayload = ref context.CommitPayloads[payload.ValidatorIndex]; + ref ExtensiblePayload existingCommitPayload = ref context.CommitPayloads[commit.ValidatorIndex]; if (existingCommitPayload != null) { if (existingCommitPayload.Hash != payload.Hash) - Log($"{nameof(OnCommitReceived)}: different commit from validator! height={payload.BlockIndex} index={payload.ValidatorIndex} view={commit.ViewNumber} existingView={existingCommitPayload.ConsensusMessage.ViewNumber}", LogLevel.Warning); + Log($"{nameof(OnCommitReceived)}: different commit from validator! height={commit.BlockIndex} index={commit.ValidatorIndex} view={commit.ViewNumber} existingView={context.GetMessage(existingCommitPayload).ViewNumber}", LogLevel.Warning); return; } @@ -246,14 +247,14 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit) if (commit.ViewNumber == context.ViewNumber) { - Log($"{nameof(OnCommitReceived)}: height={payload.BlockIndex} view={commit.ViewNumber} index={payload.ValidatorIndex} nc={context.CountCommitted} nf={context.CountFailed}"); + Log($"{nameof(OnCommitReceived)}: height={commit.BlockIndex} view={commit.ViewNumber} index={commit.ValidatorIndex} nc={context.CountCommitted} nf={context.CountFailed}"); byte[] hashData = context.EnsureHeader()?.GetHashData(); if (hashData == null) { existingCommitPayload = payload; } - else if (Crypto.VerifySignature(hashData, commit.Signature, context.Validators[payload.ValidatorIndex])) + else if (Crypto.VerifySignature(hashData, commit.Signature, context.Validators[commit.ValidatorIndex])) { existingCommitPayload = payload; CheckCommits(); @@ -261,7 +262,7 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit) return; } // Receiving commit from another view - Log($"{nameof(OnCommitReceived)}: record commit for different view={commit.ViewNumber} index={payload.ValidatorIndex} height={payload.BlockIndex}"); + Log($"{nameof(OnCommitReceived)}: record commit for different view={commit.ViewNumber} index={commit.ValidatorIndex} height={commit.BlockIndex}"); existingCommitPayload = payload; } @@ -273,23 +274,13 @@ private void ExtendTimerByFactor(int maxDelayInBlockTimes) ChangeTimer(nextDelay); } - private void OnConsensusPayload(ConsensusPayload payload) + private void OnConsensusPayload(ExtensiblePayload payload) { if (context.BlockSent) return; - if (payload.Version != context.Block.Version) return; - if (payload.PrevHash != context.Block.PrevHash || payload.BlockIndex != context.Block.Index) - { - if (context.Block.Index < payload.BlockIndex) - { - Log($"chain sync: expected={payload.BlockIndex} current={context.Block.Index - 1} nodes={LocalNode.Singleton.ConnectedCount}", LogLevel.Warning); - } - return; - } - if (payload.ValidatorIndex >= context.Validators.Length) return; ConsensusMessage message; try { - message = payload.ConsensusMessage; + message = context.GetMessage(payload); } catch (FormatException) { @@ -299,10 +290,17 @@ private void OnConsensusPayload(ConsensusPayload payload) { return; } - context.LastSeenMessage[context.Validators[payload.ValidatorIndex]] = payload.BlockIndex; - foreach (IP2PPlugin plugin in Plugin.P2PPlugins) - if (!plugin.OnConsensusMessage(payload)) - return; + if (message.BlockIndex != context.Block.Index) + { + if (context.Block.Index < message.BlockIndex) + { + Log($"chain sync: expected={message.BlockIndex} current={context.Block.Index - 1} nodes={LocalNode.Singleton.ConnectedCount}", LogLevel.Warning); + } + return; + } + if (message.ValidatorIndex >= context.Validators.Length) return; + if (payload.Sender != Contract.CreateSignatureRedeemScript(context.Validators[message.ValidatorIndex]).ToScriptHash()) return; + context.LastSeenMessage[context.Validators[message.ValidatorIndex]] = message.BlockIndex; switch (message) { case ChangeView view: @@ -317,11 +315,11 @@ private void OnConsensusPayload(ConsensusPayload payload) case Commit commit: OnCommitReceived(payload, commit); break; - case RecoveryRequest _: - OnRecoveryRequestReceived(payload); + case RecoveryRequest request: + OnRecoveryRequestReceived(payload, request); break; case RecoveryMessage recovery: - OnRecoveryMessageReceived(payload, recovery); + OnRecoveryMessageReceived(recovery); break; } } @@ -333,29 +331,29 @@ private void OnPersistCompleted(Block block) InitializeConsensus(0); } - private void OnRecoveryMessageReceived(ConsensusPayload payload, RecoveryMessage message) + private void OnRecoveryMessageReceived(RecoveryMessage message) { // isRecovering is always set to false again after OnRecoveryMessageReceived isRecovering = true; int validChangeViews = 0, totalChangeViews = 0, validPrepReq = 0, totalPrepReq = 0; int validPrepResponses = 0, totalPrepResponses = 0, validCommits = 0, totalCommits = 0; - Log($"{nameof(OnRecoveryMessageReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); + Log($"{nameof(OnRecoveryMessageReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex}"); try { if (message.ViewNumber > context.ViewNumber) { if (context.CommitSent) return; - ConsensusPayload[] changeViewPayloads = message.GetChangeViewPayloads(context, payload); + ExtensiblePayload[] changeViewPayloads = message.GetChangeViewPayloads(context); totalChangeViews = changeViewPayloads.Length; - foreach (ConsensusPayload changeViewPayload in changeViewPayloads) + foreach (ExtensiblePayload changeViewPayload in changeViewPayloads) if (ReverifyAndProcessPayload(changeViewPayload)) validChangeViews++; } if (message.ViewNumber == context.ViewNumber && !context.NotAcceptingPayloadsDueToViewChanging && !context.CommitSent) { if (!context.RequestSentOrReceived) { - ConsensusPayload prepareRequestPayload = message.GetPrepareRequestPayload(context, payload); + ExtensiblePayload prepareRequestPayload = message.GetPrepareRequestPayload(context); if (prepareRequestPayload != null) { totalPrepReq = 1; @@ -364,17 +362,17 @@ private void OnRecoveryMessageReceived(ConsensusPayload payload, RecoveryMessage else if (context.IsPrimary) SendPrepareRequest(); } - ConsensusPayload[] prepareResponsePayloads = message.GetPrepareResponsePayloads(context, payload); + ExtensiblePayload[] prepareResponsePayloads = message.GetPrepareResponsePayloads(context); totalPrepResponses = prepareResponsePayloads.Length; - foreach (ConsensusPayload prepareResponsePayload in prepareResponsePayloads) + foreach (ExtensiblePayload prepareResponsePayload in prepareResponsePayloads) if (ReverifyAndProcessPayload(prepareResponsePayload)) validPrepResponses++; } if (message.ViewNumber <= context.ViewNumber) { // Ensure we know about all commits from lower view numbers. - ConsensusPayload[] commitPayloads = message.GetCommitPayloadsFromRecoveryMessage(context, payload); + ExtensiblePayload[] commitPayloads = message.GetCommitPayloadsFromRecoveryMessage(context); totalCommits = commitPayloads.Length; - foreach (ConsensusPayload commitPayload in commitPayloads) + foreach (ExtensiblePayload commitPayload in commitPayloads) if (ReverifyAndProcessPayload(commitPayload)) validCommits++; } } @@ -389,7 +387,7 @@ private void OnRecoveryMessageReceived(ConsensusPayload payload, RecoveryMessage } } - private void OnRecoveryRequestReceived(ConsensusPayload payload) + private void OnRecoveryRequestReceived(ExtensiblePayload payload, ConsensusMessage message) { // We keep track of the payload hashes received in this block, and don't respond with recovery // in response to the same payload that we already responded to previously. @@ -399,7 +397,7 @@ private void OnRecoveryRequestReceived(ConsensusPayload payload) // additional recovery message response. if (!knownHashes.Add(payload.Hash)) return; - Log($"On{payload.ConsensusMessage.GetType().Name}Received: height={payload.BlockIndex} index={payload.ValidatorIndex} view={payload.ConsensusMessage.ViewNumber}"); + Log($"On{message.GetType().Name}Received: height={message.BlockIndex} index={message.ValidatorIndex} view={message.ViewNumber}"); if (context.WatchOnly) return; if (!context.CommitSent) { @@ -408,7 +406,7 @@ private void OnRecoveryRequestReceived(ConsensusPayload payload) // Limit recoveries to be sent from an upper limit of `f` nodes for (int i = 1; i <= allowedRecoveryNodeCount; i++) { - var chosenIndex = (payload.ValidatorIndex + i) % context.Validators.Length; + var chosenIndex = (message.ValidatorIndex + i) % context.Validators.Length; if (chosenIndex != context.MyIndex) continue; shouldSendRecovery = true; break; @@ -420,11 +418,12 @@ private void OnRecoveryRequestReceived(ConsensusPayload payload) localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryMessage() }); } - private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest message) + private void OnPrepareRequestReceived(ExtensiblePayload payload, PrepareRequest message) { if (context.RequestSentOrReceived || context.NotAcceptingPayloadsDueToViewChanging) return; - if (payload.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex || message.ViewNumber != context.ViewNumber) return; - Log($"{nameof(OnPrepareRequestReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} tx={message.TransactionHashes.Length}"); + if (message.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex || message.ViewNumber != context.ViewNumber) return; + if (message.Version != context.Block.Version || message.PrevHash != context.Block.PrevHash) return; + Log($"{nameof(OnPrepareRequestReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex} tx={message.TransactionHashes.Length}"); if (message.Timestamp <= context.PrevHeader.Timestamp || message.Timestamp > TimeProvider.Current.UtcNow.AddMilliseconds(8 * Blockchain.MillisecondsPerBlock).ToTimestampMS()) { Log($"Timestamp incorrect: {message.Timestamp}", LogLevel.Warning); @@ -447,13 +446,13 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m context.VerificationContext = new TransactionVerificationContext(); for (int i = 0; i < context.PreparationPayloads.Length; i++) if (context.PreparationPayloads[i] != null) - if (!context.PreparationPayloads[i].GetDeserializedMessage().PreparationHash.Equals(payload.Hash)) + if (!context.GetMessage(context.PreparationPayloads[i]).PreparationHash.Equals(payload.Hash)) context.PreparationPayloads[i] = null; - context.PreparationPayloads[payload.ValidatorIndex] = payload; + context.PreparationPayloads[message.ValidatorIndex] = payload; byte[] hashData = context.EnsureHeader().GetHashData(); for (int i = 0; i < context.CommitPayloads.Length; i++) - if (context.CommitPayloads[i]?.ConsensusMessage.ViewNumber == context.ViewNumber) - if (!Crypto.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage().Signature, context.Validators[i])) + if (context.GetMessage(context.CommitPayloads[i])?.ViewNumber == context.ViewNumber) + if (!Crypto.VerifySignature(hashData, context.GetMessage(context.CommitPayloads[i]).Signature, context.Validators[i])) context.CommitPayloads[i] = null; if (context.TransactionHashes.Length == 0) @@ -491,10 +490,10 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m } } - private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse message) + private void OnPrepareResponseReceived(ExtensiblePayload payload, PrepareResponse message) { if (message.ViewNumber != context.ViewNumber) return; - if (context.PreparationPayloads[payload.ValidatorIndex] != null || context.NotAcceptingPayloadsDueToViewChanging) return; + if (context.PreparationPayloads[message.ValidatorIndex] != null || context.NotAcceptingPayloadsDueToViewChanging) return; if (context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex] != null && !message.PreparationHash.Equals(context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex].Hash)) return; @@ -502,8 +501,8 @@ private void OnPrepareResponseReceived(ConsensusPayload payload, PrepareResponse // around 2*15/M=30.0/5 ~ 40% block time (for M=5) ExtendTimerByFactor(2); - Log($"{nameof(OnPrepareResponseReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex}"); - context.PreparationPayloads[payload.ValidatorIndex] = payload; + Log($"{nameof(OnPrepareResponseReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex}"); + context.PreparationPayloads[message.ValidatorIndex] = payload; if (context.WatchOnly || context.CommitSent) return; if (context.RequestSentOrReceived) CheckPreparations(); @@ -534,7 +533,7 @@ protected override void OnReceive(object message) OnPersistCompleted(completed.Block); break; case Blockchain.RelayResult rr: - if (rr.Result == VerifyResult.Succeed && rr.Inventory is ConsensusPayload payload) + if (rr.Result == VerifyResult.Succeed && rr.Inventory is ExtensiblePayload payload && payload.Category == "Consensus") OnConsensusPayload(payload); break; } @@ -647,7 +646,7 @@ private void RequestChangeView(ChangeViewReason reason) CheckExpectedView(expectedView); } - private bool ReverifyAndProcessPayload(ConsensusPayload payload) + private bool ReverifyAndProcessPayload(ExtensiblePayload payload) { if (!payload.Verify(context.Snapshot)) return false; OnConsensusPayload(payload); @@ -682,7 +681,7 @@ internal protected override bool IsHighPriority(object message) { switch (message) { - case ConsensusPayload _: + case ExtensiblePayload _: case ConsensusService.SetViewNumber _: case ConsensusService.Timer _: case Blockchain.PersistCompleted _: diff --git a/src/neo/Consensus/PrepareRequest.cs b/src/neo/Consensus/PrepareRequest.cs index 2369b4f9f7..9e96ca4202 100644 --- a/src/neo/Consensus/PrepareRequest.cs +++ b/src/neo/Consensus/PrepareRequest.cs @@ -8,12 +8,16 @@ namespace Neo.Consensus { public class PrepareRequest : ConsensusMessage { + public uint Version; + public UInt256 PrevHash; public ulong Timestamp; public ulong Nonce; public UInt256[] TransactionHashes; public override int Size => base.Size - + sizeof(ulong) //Timestamp + + sizeof(uint) //Version + + UInt256.Length //PrevHash + + sizeof(ulong) //Timestamp + sizeof(ulong) //Nonce + TransactionHashes.GetVarSize(); //TransactionHashes @@ -25,6 +29,8 @@ public PrepareRequest() public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); + Version = reader.ReadUInt32(); + PrevHash = reader.ReadSerializable(); Timestamp = reader.ReadUInt64(); Nonce = reader.ReadUInt64(); TransactionHashes = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); @@ -35,6 +41,8 @@ public override void Deserialize(BinaryReader reader) public override void Serialize(BinaryWriter writer) { base.Serialize(writer); + writer.Write(Version); + writer.Write(PrevHash); writer.Write(Timestamp); writer.Write(Nonce); writer.Write(TransactionHashes); diff --git a/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs index 16d392fce5..8419193de6 100644 --- a/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Network.P2P.Payloads; using System; using System.IO; @@ -30,18 +29,6 @@ void ISerializable.Deserialize(BinaryReader reader) InvocationScript = reader.ReadVarBytes(1024); } - public static ChangeViewPayloadCompact FromPayload(ConsensusPayload payload) - { - ChangeView message = payload.GetDeserializedMessage(); - return new ChangeViewPayloadCompact - { - ValidatorIndex = payload.ValidatorIndex, - OriginalViewNumber = message.ViewNumber, - Timestamp = message.Timestamp, - InvocationScript = payload.Witness.InvocationScript - }; - } - void ISerializable.Serialize(BinaryWriter writer) { writer.Write(ValidatorIndex); diff --git a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs index 58754691da..81b3bc37af 100644 --- a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Network.P2P.Payloads; using System; using System.IO; @@ -30,18 +29,6 @@ void ISerializable.Deserialize(BinaryReader reader) InvocationScript = reader.ReadVarBytes(1024); } - public static CommitPayloadCompact FromPayload(ConsensusPayload payload) - { - Commit message = payload.GetDeserializedMessage(); - return new CommitPayloadCompact - { - ViewNumber = message.ViewNumber, - ValidatorIndex = payload.ValidatorIndex, - Signature = message.Signature, - InvocationScript = payload.Witness.InvocationScript - }; - } - void ISerializable.Serialize(BinaryWriter writer) { writer.Write(ViewNumber); diff --git a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs index f364c88385..72fbd2de2f 100644 --- a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Network.P2P.Payloads; using System; using System.IO; @@ -24,15 +23,6 @@ void ISerializable.Deserialize(BinaryReader reader) InvocationScript = reader.ReadVarBytes(1024); } - public static PreparationPayloadCompact FromPayload(ConsensusPayload payload) - { - return new PreparationPayloadCompact - { - ValidatorIndex = payload.ValidatorIndex, - InvocationScript = payload.Witness.InvocationScript - }; - } - void ISerializable.Serialize(BinaryWriter writer) { writer.Write(ValidatorIndex); diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs index 34e2e0da9a..11b8bd0361 100644 --- a/src/neo/Consensus/RecoveryMessage.cs +++ b/src/neo/Consensus/RecoveryMessage.cs @@ -1,6 +1,6 @@ using Neo.IO; using Neo.Network.P2P.Payloads; -using Neo.SmartContract; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -45,89 +45,47 @@ public override void Deserialize(BinaryReader reader) CommitMessages = reader.ReadSerializableArray(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); } - internal ConsensusPayload[] GetChangeViewPayloads(ConsensusContext context, ConsensusPayload payload) + internal ExtensiblePayload[] GetChangeViewPayloads(ConsensusContext context) { - return ChangeViewMessages.Values.Select(p => new ConsensusPayload + return ChangeViewMessages.Values.Select(p => context.CreatePayload(new ChangeView { - Version = payload.Version, - PrevHash = payload.PrevHash, - BlockIndex = payload.BlockIndex, + BlockIndex = BlockIndex, ValidatorIndex = p.ValidatorIndex, - ConsensusMessage = new ChangeView - { - ViewNumber = p.OriginalViewNumber, - Timestamp = p.Timestamp - }, - Witness = new Witness - { - InvocationScript = p.InvocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[p.ValidatorIndex]) - } - }).ToArray(); + ViewNumber = p.OriginalViewNumber, + Timestamp = p.Timestamp + }, p.InvocationScript)).ToArray(); } - internal ConsensusPayload[] GetCommitPayloadsFromRecoveryMessage(ConsensusContext context, ConsensusPayload payload) + internal ExtensiblePayload[] GetCommitPayloadsFromRecoveryMessage(ConsensusContext context) { - return CommitMessages.Values.Select(p => new ConsensusPayload + return CommitMessages.Values.Select(p => context.CreatePayload(new Commit { - Version = payload.Version, - PrevHash = payload.PrevHash, - BlockIndex = payload.BlockIndex, + BlockIndex = BlockIndex, ValidatorIndex = p.ValidatorIndex, - ConsensusMessage = new Commit - { - ViewNumber = p.ViewNumber, - Signature = p.Signature - }, - Witness = new Witness - { - InvocationScript = p.InvocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[p.ValidatorIndex]) - } - }).ToArray(); + ViewNumber = p.ViewNumber, + Signature = p.Signature + }, p.InvocationScript)).ToArray(); } - internal ConsensusPayload GetPrepareRequestPayload(ConsensusContext context, ConsensusPayload payload) + internal ExtensiblePayload GetPrepareRequestPayload(ConsensusContext context) { if (PrepareRequestMessage == null) return null; if (!PreparationMessages.TryGetValue(context.Block.ConsensusData.PrimaryIndex, out PreparationPayloadCompact compact)) return null; - return new ConsensusPayload - { - Version = payload.Version, - PrevHash = payload.PrevHash, - BlockIndex = payload.BlockIndex, - ValidatorIndex = context.Block.ConsensusData.PrimaryIndex, - ConsensusMessage = PrepareRequestMessage, - Witness = new Witness - { - InvocationScript = compact.InvocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[context.Block.ConsensusData.PrimaryIndex]) - } - }; + return context.CreatePayload(PrepareRequestMessage, compact.InvocationScript); } - internal ConsensusPayload[] GetPrepareResponsePayloads(ConsensusContext context, ConsensusPayload payload) + internal ExtensiblePayload[] GetPrepareResponsePayloads(ConsensusContext context) { UInt256 preparationHash = PreparationHash ?? context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex]?.Hash; - if (preparationHash is null) return new ConsensusPayload[0]; - return PreparationMessages.Values.Where(p => p.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex).Select(p => new ConsensusPayload + if (preparationHash is null) return Array.Empty(); + return PreparationMessages.Values.Where(p => p.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex).Select(p => context.CreatePayload(new PrepareResponse { - Version = payload.Version, - PrevHash = payload.PrevHash, - BlockIndex = payload.BlockIndex, + BlockIndex = BlockIndex, ValidatorIndex = p.ValidatorIndex, - ConsensusMessage = new PrepareResponse - { - ViewNumber = ViewNumber, - PreparationHash = preparationHash - }, - Witness = new Witness - { - InvocationScript = p.InvocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[p.ValidatorIndex]) - } - }).ToArray(); + ViewNumber = ViewNumber, + PreparationHash = preparationHash + }, p.InvocationScript)).ToArray(); } public override void Serialize(BinaryWriter writer) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 01898679f7..c475228b79 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -561,7 +561,7 @@ internal protected override bool IsHighPriority(object message) switch (message) { case Block _: - case ConsensusPayload _: + case ExtensiblePayload _: case Terminated _: return true; default: diff --git a/src/neo/Network/P2P/Message.cs b/src/neo/Network/P2P/Message.cs index ed0d23f883..15a7e0783a 100644 --- a/src/neo/Network/P2P/Message.cs +++ b/src/neo/Network/P2P/Message.cs @@ -38,7 +38,6 @@ public static Message Create(MessageCommand command, ISerializable payload = nul bool tryCompression = command == MessageCommand.Block || - command == MessageCommand.Consensus || command == MessageCommand.Extensible || command == MessageCommand.Transaction || command == MessageCommand.Headers || diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index 7438d90850..04856c4a48 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -38,8 +38,6 @@ public enum MessageCommand : byte Transaction = 0x2b, [ReflectionCache(typeof(Block))] Block = 0x2c, - [ReflectionCache(typeof(ConsensusPayload))] - Consensus = 0x2d, [ReflectionCache(typeof(ExtensiblePayload))] Extensible = 0x2e, Reject = 0x2f, diff --git a/src/neo/Network/P2P/Payloads/ConsensusPayload.cs b/src/neo/Network/P2P/Payloads/ConsensusPayload.cs deleted file mode 100644 index dbf2732fbc..0000000000 --- a/src/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ /dev/null @@ -1,130 +0,0 @@ -using Neo.Consensus; -using Neo.Cryptography; -using Neo.Cryptography.ECC; -using Neo.IO; -using Neo.Persistence; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using System; -using System.IO; - -namespace Neo.Network.P2P.Payloads -{ - public class ConsensusPayload : IInventory - { - public uint Version; - public UInt256 PrevHash; - public uint BlockIndex; - public byte ValidatorIndex; - public byte[] Data; - public Witness Witness; - - private ConsensusMessage _deserializedMessage = null; - public ConsensusMessage ConsensusMessage - { - get - { - if (_deserializedMessage is null) - _deserializedMessage = ConsensusMessage.DeserializeFrom(Data); - return _deserializedMessage; - } - internal set - { - if (!ReferenceEquals(_deserializedMessage, value)) - { - _deserializedMessage = value; - Data = value?.ToArray(); - } - } - } - - private UInt256 _hash = null; - public UInt256 Hash - { - get - { - if (_hash == null) - { - _hash = this.CalculateHash(); - } - return _hash; - } - } - - InventoryType IInventory.InventoryType => InventoryType.Consensus; - - public int Size => - sizeof(uint) + //Version - PrevHash.Size + //PrevHash - sizeof(uint) + //BlockIndex - sizeof(byte) + //ValidatorIndex - Data.GetVarSize() + //Data - 1 + Witness.Size; //Witness - - Witness[] IVerifiable.Witnesses - { - get - { - return new[] { Witness }; - } - set - { - if (value.Length != 1) throw new ArgumentException(); - Witness = value[0]; - } - } - - public T GetDeserializedMessage() where T : ConsensusMessage - { - return (T)ConsensusMessage; - } - - void ISerializable.Deserialize(BinaryReader reader) - { - ((IVerifiable)this).DeserializeUnsigned(reader); - if (reader.ReadByte() != 1) throw new FormatException(); - Witness = reader.ReadSerializable(); - } - - void IVerifiable.DeserializeUnsigned(BinaryReader reader) - { - Version = reader.ReadUInt32(); - PrevHash = reader.ReadSerializable(); - BlockIndex = reader.ReadUInt32(); - ValidatorIndex = reader.ReadByte(); - if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount) - throw new FormatException(); - Data = reader.ReadVarBytes(); - } - - UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot) - { - ECPoint[] validators = NativeContract.NEO.GetNextBlockValidators(snapshot); - if (validators.Length <= ValidatorIndex) - throw new InvalidOperationException(); - return new[] { Contract.CreateSignatureRedeemScript(validators[ValidatorIndex]).ToScriptHash() }; - } - - void ISerializable.Serialize(BinaryWriter writer) - { - ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write((byte)1); writer.Write(Witness); - } - - void IVerifiable.SerializeUnsigned(BinaryWriter writer) - { - writer.Write(Version); - writer.Write(PrevHash); - writer.Write(BlockIndex); - writer.Write(ValidatorIndex); - writer.WriteVarBytes(Data); - } - - public bool Verify(StoreView snapshot) - { - if (BlockIndex <= snapshot.Height) - return false; - return this.VerifyWitnesses(snapshot, 0_02000000); - } - } -} diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs index 6b21a81276..c5414770b2 100644 --- a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -64,7 +64,7 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) Category = reader.ReadVarString(32); ValidBlockStart = reader.ReadUInt32(); ValidBlockEnd = reader.ReadUInt32(); - if (ValidBlockStart > ValidBlockEnd) throw new FormatException(); + if (ValidBlockStart >= ValidBlockEnd) throw new FormatException(); Sender = reader.ReadSerializable(); Data = reader.ReadVarBytes(ushort.MaxValue); } diff --git a/src/neo/Network/P2P/Payloads/InventoryType.cs b/src/neo/Network/P2P/Payloads/InventoryType.cs index 33b9f2fc25..3e6bfa423f 100644 --- a/src/neo/Network/P2P/Payloads/InventoryType.cs +++ b/src/neo/Network/P2P/Payloads/InventoryType.cs @@ -4,7 +4,6 @@ public enum InventoryType : byte { TX = MessageCommand.Transaction, Block = MessageCommand.Block, - Extensible = MessageCommand.Extensible, - Consensus = MessageCommand.Consensus + Extensible = MessageCommand.Extensible } } diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 48e1cb3143..b0b1051c71 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -62,7 +62,6 @@ private void OnMessage(Message msg) OnAddrMessageReceived((AddrPayload)msg.Payload); break; case MessageCommand.Block: - case MessageCommand.Consensus: case MessageCommand.Extensible: OnInventoryReceived((IInventory)msg.Payload); break; diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index 8e1343ec0b..5fdc088b92 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -86,7 +86,6 @@ private void EnqueueMessage(Message message) switch (message.Command) { case MessageCommand.Alert: - case MessageCommand.Consensus: case MessageCommand.Extensible: case MessageCommand.FilterAdd: case MessageCommand.FilterClear: @@ -223,7 +222,6 @@ internal protected override bool IsHighPriority(object message) case Message msg: switch (msg.Command) { - case MessageCommand.Consensus: case MessageCommand.Extensible: case MessageCommand.FilterAdd: case MessageCommand.FilterClear: diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 688fa13cff..07088a9fb5 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -342,7 +342,7 @@ internal protected override bool IsHighPriority(object message) case TaskManager.RestartTasks _: return true; case TaskManager.NewTasks tasks: - if (tasks.Payload.Type == InventoryType.Block || tasks.Payload.Type == InventoryType.Consensus || tasks.Payload.Type == InventoryType.Extensible) + if (tasks.Payload.Type == InventoryType.Block || tasks.Payload.Type == InventoryType.Extensible) return true; return false; default: diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs index d6ba709e36..8323339d75 100644 --- a/src/neo/Plugins/IP2PPlugin.cs +++ b/src/neo/Plugins/IP2PPlugin.cs @@ -6,7 +6,6 @@ namespace Neo.Plugins public interface IP2PPlugin { bool OnP2PMessage(Message message) => true; - bool OnConsensusMessage(ConsensusPayload payload) => true; void OnVerifiedInventory(IInventory inventory); } } diff --git a/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs b/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs index dba8523f25..757d041801 100644 --- a/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs +++ b/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Consensus; using Neo.IO; -using Neo.Network.P2P.Payloads; namespace Neo.UnitTests.Consensus { @@ -29,18 +28,6 @@ public void DeserializeAndSerialize() Assert.AreEqual(test.ValidatorIndex, clone.ValidatorIndex); Assert.AreEqual(test.OriginalViewNumber, clone.OriginalViewNumber); CollectionAssert.AreEqual(test.InvocationScript, clone.InvocationScript); - - clone = RecoveryMessage.ChangeViewPayloadCompact.FromPayload(new ConsensusPayload() - { - Data = new ChangeView() { Timestamp = 1, ViewNumber = 3 }.ToArray(), - ValidatorIndex = 2, - Witness = new Witness() { InvocationScript = new byte[] { 1, 2, 3 } } - }); - - Assert.AreEqual(test.Timestamp, clone.Timestamp); - Assert.AreEqual(test.ValidatorIndex, clone.ValidatorIndex); - Assert.AreEqual(test.OriginalViewNumber, clone.OriginalViewNumber); - CollectionAssert.AreEqual(test.InvocationScript, clone.InvocationScript); } } } diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 85f066a675..a900f90797 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -28,15 +28,44 @@ namespace Neo.UnitTests.Consensus [TestClass] public class ConsensusTests : TestKit { + private KeyPair[] kp_array; + [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); + + var moked = new ECPoint[] { + ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", Neo.Cryptography.ECC.ECCurve.Secp256r1), + ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", Neo.Cryptography.ECC.ECCurve.Secp256r1) + }; + + kp_array = new KeyPair[7] + { + UT_Crypto.generateKey(32), // not used, kept for index consistency, didactically + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32), + UT_Crypto.generateKey(32) + }.OrderBy(p => p.PublicKey).ToArray(); + + TestBlockchain.AddWhiteList(TestBlockchain.DefaultExtensibleWitnessWhiteList + .Concat(moked.Select(u => Contract.CreateSignatureContract(u).ScriptHash)) + .Concat(kp_array.Select(u => Contract.CreateSignatureContract(u.PublicKey).ScriptHash)) + .ToArray()); } [TestCleanup] public void Cleanup() { + TestBlockchain.AddWhiteList(TestBlockchain.DefaultExtensibleWitnessWhiteList); Shutdown(); } @@ -48,18 +77,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine($"\n(UT-Consensus) Wallet is: {mockWallet.Object.GetAccount(UInt160.Zero).GetKey().PublicKey}"); var mockContext = new Mock(mockWallet.Object, Blockchain.Singleton.Store); - - KeyPair[] kp_array = new KeyPair[7] - { - UT_Crypto.generateKey(32), // not used, kept for index consistency, didactically - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32) - }.OrderBy(p => p.PublicKey).ToArray(); - var timeValues = new[] { new DateTime(1980, 06, 01, 0, 0, 1, 001, DateTimeKind.Utc), // For tests, used below new DateTime(1980, 06, 01, 0, 0, 3, 001, DateTimeKind.Utc), // For receiving block @@ -67,7 +84,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm new DateTime(1980, 06, 01, 0, 0, 15, 001, DateTimeKind.Utc), // unused }; for (int i = 0; i < timeValues.Length; i++) - Console.WriteLine($"time {i}: {timeValues[i].ToString()} "); + Console.WriteLine($"time {i}: {timeValues[i]} "); ulong defaultTimestamp = 328665601001; // GMT: Sunday, June 1, 1980 12:00:01.001 AM // check basic ConsensusContext // mockConsensusContext.Object.block_received_time.ToTimestamp().Should().Be(4244941697); //1968-06-01 00:00:01 @@ -142,15 +159,15 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.LastSeenMessage[validator] = 0; } // Ensuring cast of type ConsensusPayload from the received message from subscriber - ConsensusPayload initialRecoveryPayload = (ConsensusPayload)askingForInitialRecovery.Inventory; + ExtensiblePayload initialRecoveryPayload = (ExtensiblePayload)askingForInitialRecovery.Inventory; // Ensuring casting of type RecoveryRequest - RecoveryRequest rrm = (RecoveryRequest)initialRecoveryPayload.ConsensusMessage; + RecoveryRequest rrm = initialRecoveryPayload.Data.AsSerializable(); rrm.Timestamp.Should().Be(defaultTimestamp); Console.WriteLine("Waiting for backup ChangeView... "); var backupOnAskingChangeView = subscriber.ExpectMsg(); - var changeViewPayload = (ConsensusPayload)backupOnAskingChangeView.Inventory; - ChangeView cvm = (ChangeView)changeViewPayload.ConsensusMessage; + var changeViewPayload = (ExtensiblePayload)backupOnAskingChangeView.Inventory; + ChangeView cvm = changeViewPayload.Data.AsSerializable(); cvm.Timestamp.Should().Be(defaultTimestamp); cvm.ViewNumber.Should().Be(0); cvm.Reason.Should().Be(ChangeViewReason.Timeout); @@ -175,42 +192,60 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(7); Console.WriteLine("\nWaiting for recovery due to failed nodes... "); var backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); - var recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; - rrm = (RecoveryRequest)recoveryPayload.ConsensusMessage; + var recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory; + rrm = recoveryPayload.Data.AsSerializable(); rrm.Timestamp.Should().Be(defaultTimestamp); Console.WriteLine("will create template MakePrepareRequest..."); mockContext.Object.PrevHeader.Timestamp = defaultTimestamp; mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0xe239c7228fa6b46cc0cf43623b2f934301d0b4f7")); var prepReq = mockContext.Object.MakePrepareRequest(); - var ppToSend = (PrepareRequest)prepReq.ConsensusMessage; + var ppToSend = prepReq.Data.AsSerializable(); // Forcing hashes to 0 because mempool is currently shared ppToSend.TransactionHashes = new UInt256[0]; ppToSend.TransactionHashes.Length.Should().Be(0); Console.WriteLine($"\nAsserting PreparationPayloads is 1 (After MakePrepareRequest)..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(1); - mockContext.Object.PreparationPayloads[prepReq.ValidatorIndex] = null; + mockContext.Object.PreparationPayloads[ppToSend.ValidatorIndex] = null; Console.WriteLine("will tell prepare request!"); + prepReq = new ExtensiblePayload + { + Category = "Consensus", + ValidBlockStart = 0, + ValidBlockEnd = ppToSend.BlockIndex, + Sender = prepReq.Sender, + Data = ppToSend.ToArray(), + Witness = prepReq.Witness + }; TellConsensusPayload(actorConsensus, prepReq); Console.WriteLine("Waiting for something related to the PrepRequest...\nNothing happens...Recovery will come due to failed nodes"); var backupOnRecoveryDueToFailedNodesII = subscriber.ExpectMsg(); - var recoveryPayloadII = (ConsensusPayload)backupOnRecoveryDueToFailedNodesII.Inventory; - rrm = (RecoveryRequest)recoveryPayloadII.ConsensusMessage; + var recoveryPayloadII = (ExtensiblePayload)backupOnRecoveryDueToFailedNodesII.Inventory; + rrm = recoveryPayloadII.Data.AsSerializable(); Console.WriteLine($"\nAsserting PreparationPayloads is 0..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(0); Console.WriteLine($"\nAsserting CountFailed is 6..."); mockContext.Object.CountFailed.Should().Be(6); Console.WriteLine("\nFailed because it is not primary and it created the prereq...Time to adjust"); - prepReq.ValidatorIndex = 1; //simulating primary as prepreq creator (signature is skip, no problem) + ppToSend.ValidatorIndex = 1; //simulating primary as prepreq creator (signature is skip, no problem) // cleaning old try with Self ValidatorIndex mockContext.Object.PreparationPayloads[mockContext.Object.MyIndex] = null; + prepReq = new ExtensiblePayload + { + Category = "Consensus", + ValidBlockStart = 0, + ValidBlockEnd = ppToSend.BlockIndex, + Sender = mockContext.Object.GetSender(ppToSend.ValidatorIndex), + Data = ppToSend.ToArray(), + Witness = prepReq.Witness + }; TellConsensusPayload(actorConsensus, prepReq); var OnPrepResponse = subscriber.ExpectMsg(); - var prepResponsePayload = (ConsensusPayload)OnPrepResponse.Inventory; - PrepareResponse prm = (PrepareResponse)prepResponsePayload.ConsensusMessage; + var prepResponsePayload = (ExtensiblePayload)OnPrepResponse.Inventory; + PrepareResponse prm = prepResponsePayload.Data.AsSerializable(); prm.PreparationHash.Should().Be(prepReq.Hash); Console.WriteLine("\nAsserting PreparationPayloads count is 2..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(2); @@ -218,11 +253,11 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(5); // Simulating CN 3 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 2)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 2)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); - recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; - rrm = (RecoveryRequest)recoveryPayload.ConsensusMessage; + recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory; + rrm = recoveryPayload.Data.AsSerializable(); rrm.Timestamp.Should().Be(defaultTimestamp); //Asserts Console.WriteLine("\nAsserting PreparationPayloads count is 3..."); @@ -231,11 +266,11 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(4); // Simulating CN 5 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 4)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 4)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); - recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; - rrm = (RecoveryRequest)recoveryPayload.ConsensusMessage; + recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory; + rrm = recoveryPayload.Data.AsSerializable(); rrm.Timestamp.Should().Be(defaultTimestamp); //Asserts Console.WriteLine("\nAsserting PreparationPayloads count is 4..."); @@ -244,10 +279,10 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(3); // Simulating CN 4 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 3)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 3)); var onCommitPayload = subscriber.ExpectMsg(); - var commitPayload = (ConsensusPayload)onCommitPayload.Inventory; - Commit cm = (Commit)commitPayload.ConsensusMessage; + var commitPayload = (ExtensiblePayload)onCommitPayload.Inventory; + Commit cm = commitPayload.Data.AsSerializable(); Console.WriteLine("\nAsserting PreparationPayloads count is 5..."); mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(5); Console.WriteLine("\nAsserting CountCommitted is 1..."); @@ -303,36 +338,36 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm // Basic tests for understanding signatures and ensuring signatures of commits are correct on tests - var cmPayloadTemp = GetCommitPayloadModifiedAndSignedCopy(commitPayload, 6, kp_array[6], updatedBlockHashData); + var cmPayloadTemp = GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 6, kp_array[6], updatedBlockHashData); Crypto.VerifySignature(originalBlockHashData, cm.Signature, mockContext.Object.Validators[0]).Should().BeFalse(); Crypto.VerifySignature(updatedBlockHashData, cm.Signature, mockContext.Object.Validators[0]).Should().BeFalse(); - Crypto.VerifySignature(originalBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[6]).Should().BeFalse(); - Crypto.VerifySignature(updatedBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[6]).Should().BeTrue(); + Crypto.VerifySignature(originalBlockHashData, cmPayloadTemp.Data.AsSerializable().Signature, mockContext.Object.Validators[6]).Should().BeFalse(); + Crypto.VerifySignature(updatedBlockHashData, cmPayloadTemp.Data.AsSerializable().Signature, mockContext.Object.Validators[6]).Should().BeTrue(); Console.WriteLine("\n=========================="); Console.WriteLine("\n=========================="); Console.WriteLine("\nCN7 simulation time"); TellConsensusPayload(actorConsensus, cmPayloadTemp); var tempPayloadToBlockAndWait = subscriber.ExpectMsg(); - var rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; - RecoveryMessage rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + var rmPayload = (ExtensiblePayload)tempPayloadToBlockAndWait.Inventory; + RecoveryMessage rmm = rmPayload.Data.AsSerializable(); Console.WriteLine("\nAsserting CountCommitted is 2..."); mockContext.Object.CountCommitted.Should().Be(2); Console.WriteLine($"\nAsserting CountFailed is 1..."); mockContext.Object.CountFailed.Should().Be(6); Console.WriteLine("\nCN6 simulation time"); - TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 5, kp_array[5], updatedBlockHashData)); + TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 5, kp_array[5], updatedBlockHashData)); tempPayloadToBlockAndWait = subscriber.ExpectMsg(); - rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; - rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + rmPayload = (ExtensiblePayload)tempPayloadToBlockAndWait.Inventory; + rmm = rmPayload.Data.AsSerializable(); Console.WriteLine("\nAsserting CountCommitted is 3..."); mockContext.Object.CountCommitted.Should().Be(3); Console.WriteLine($"\nAsserting CountFailed is 0..."); mockContext.Object.CountFailed.Should().Be(5); Console.WriteLine("\nCN5 simulation time"); - TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 4, kp_array[4], updatedBlockHashData)); + TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 4, kp_array[4], updatedBlockHashData)); tempPayloadToBlockAndWait = subscriber.ExpectMsg(); Console.WriteLine("\nAsserting CountCommitted is 4..."); mockContext.Object.CountCommitted.Should().Be(4); @@ -341,11 +376,11 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm // Testing commit with wrong signature not valid // It will be invalid signature because we did not change ECPoint Console.WriteLine("\nCN4 simulation time. Wrong signature, KeyPair is not known"); - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(commitPayload, 3)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, commitPayload, 3)); Console.WriteLine("\nWaiting for recovery due to failed nodes... "); var backupOnRecoveryMessageAfterCommit = subscriber.ExpectMsg(); - rmPayload = (ConsensusPayload)backupOnRecoveryMessageAfterCommit.Inventory; - rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + rmPayload = (ExtensiblePayload)backupOnRecoveryMessageAfterCommit.Inventory; + rmm = rmPayload.Data.AsSerializable(); Console.WriteLine("\nAsserting CountCommitted is 4 (Again)..."); mockContext.Object.CountCommitted.Should().Be(4); Console.WriteLine("\nAsserting recovery message Preparation is 5..."); @@ -360,7 +395,6 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm var oldPrevHash = mockContext.Object.Block.PrevHash; mockContext.Object.Block.PrevHash = UInt256.Zero; //Payload should also be forced, otherwise OnConsensus will not pass - commitPayload.PrevHash = UInt256.Zero; Console.WriteLine($"\nNew Hash is {mockContext.Object.Block.GetHashData().ToScriptHash()}"); Console.WriteLine($"\nForcing block VerificationScript to {updatedContract.Script.ToScriptHash()}"); // The default behavior for BlockBase, when PrevHash = UInt256.Zero, is to use its own Witness @@ -370,7 +404,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine($"\nNew Hash is {mockContext.Object.Block.GetHashData().ToScriptHash()}"); Console.WriteLine("\nCN4 simulation time - Final needed signatures"); - TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData())); + TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData())); Console.WriteLine("\nWait for subscriber Block"); var utBlock = subscriber.ExpectMsg(); @@ -383,7 +417,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm // ============================================= Console.WriteLine("\nRecovery simulation..."); - mockContext.Object.CommitPayloads = new ConsensusPayload[mockContext.Object.Validators.Length]; + mockContext.Object.CommitPayloads = new ExtensiblePayload[mockContext.Object.Validators.Length]; // avoiding the BlockSent flag mockContext.Object.Block.Transactions = null; // ensuring same hash as snapshot @@ -402,8 +436,8 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("\nWaiting for RecoveryRequest before final asserts..."); var onRecoveryRequestAfterRecovery = subscriber.ExpectMsg(); - var rrPayload = (ConsensusPayload)onRecoveryRequestAfterRecovery.Inventory; - var rrMessage = (RecoveryRequest)rrPayload.ConsensusMessage; + var rrPayload = (ExtensiblePayload)onRecoveryRequestAfterRecovery.Inventory; + var rrMessage = rrPayload.Data.AsSerializable(); // It should be 3 because the commit generated by the default wallet is still invalid Console.WriteLine("\nAsserting CountCommitted is 3 (after recovery)..."); @@ -443,16 +477,18 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm /// new ValidatorIndex for the cpToCopy /// KeyPair that will be used for signing the Commit message used for creating blocks /// HashCode of the Block that is being produced and current being signed - public ConsensusPayload GetCommitPayloadModifiedAndSignedCopy(ConsensusPayload cpToCopy, byte vI, KeyPair kp, byte[] blockHashToSign) + public ExtensiblePayload GetCommitPayloadModifiedAndSignedCopy(ConsensusContext context, ExtensiblePayload cpToCopy, byte vI, KeyPair kp, byte[] blockHashToSign) { - var cpCommitTemp = cpToCopy.ToArray().AsSerializable(); - cpCommitTemp.ValidatorIndex = vI; - var oldViewNumber = ((Commit)cpCommitTemp.ConsensusMessage).ViewNumber; - cpCommitTemp.ConsensusMessage = new Commit + var cpCommitTemp = cpToCopy.ToArray().AsSerializable(); + cpCommitTemp.Sender = context.GetSender(vI); + var oldMessage = cpCommitTemp.Data.AsSerializable(); + cpCommitTemp.Data = new Commit { - ViewNumber = oldViewNumber, + BlockIndex = oldMessage.BlockIndex, + ValidatorIndex = vI, + ViewNumber = oldMessage.ViewNumber, Signature = Crypto.Sign(blockHashToSign, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()) - }; + }.ToArray(); SignPayload(cpCommitTemp, kp); return cpCommitTemp; } @@ -462,14 +498,17 @@ public ConsensusPayload GetCommitPayloadModifiedAndSignedCopy(ConsensusPayload c /// /// ConsensusPayload that will be modified /// new ValidatorIndex for the cpToCopy - public ConsensusPayload GetPayloadAndModifyValidator(ConsensusPayload cpToCopy, byte vI) + public ExtensiblePayload GetPayloadAndModifyValidator(ConsensusContext context, ExtensiblePayload cpToCopy, byte vI) { - var cpTemp = cpToCopy.ToArray().AsSerializable(); - cpTemp.ValidatorIndex = vI; + var cpTemp = cpToCopy.ToArray().AsSerializable(); + var message = ConsensusMessage.DeserializeFrom(cpTemp.Data); + message.ValidatorIndex = vI; + cpTemp.Data = message.ToArray(); + cpTemp.Sender = context.GetSender(vI); return cpTemp; } - private void SignPayload(ConsensusPayload payload, KeyPair kp) + private void SignPayload(ExtensiblePayload payload, KeyPair kp) { ContractParametersContext sc; try @@ -529,9 +568,10 @@ public void TestSerializeAndDeserializeConsensusContext() // consensusContext.TransactionHashes = new UInt256[2] {testTx1.Hash, testTx2.Hash}; consensusContext.Transactions = txs.ToDictionary(p => p.Hash); - consensusContext.PreparationPayloads = new ConsensusPayload[consensusContext.Validators.Length]; + consensusContext.PreparationPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; var prepareRequestMessage = new PrepareRequest { + PrevHash = consensusContext.Block.PrevHash, TransactionHashes = consensusContext.TransactionHashes, Timestamp = 23 }; @@ -543,7 +583,7 @@ public void TestSerializeAndDeserializeConsensusContext() consensusContext.PreparationPayloads[4] = null; consensusContext.PreparationPayloads[5] = null; - consensusContext.CommitPayloads = new ConsensusPayload[consensusContext.Validators.Length]; + consensusContext.CommitPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; using (SHA256 sha256 = SHA256.Create()) { consensusContext.CommitPayloads[3] = MakeSignedPayload(consensusContext, new Commit { Signature = sha256.ComputeHash(testTx1.Hash.ToArray()).Concat(sha256.ComputeHash(testTx1.Hash.ToArray())).ToArray() }, 3, new[] { (byte)'3', (byte)'4' }); @@ -552,7 +592,7 @@ public void TestSerializeAndDeserializeConsensusContext() consensusContext.Block.Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS(); - consensusContext.ChangeViewPayloads = new ConsensusPayload[consensusContext.Validators.Length]; + consensusContext.ChangeViewPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; consensusContext.ChangeViewPayloads[0] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 6 }, 0, new[] { (byte)'A' }); consensusContext.ChangeViewPayloads[1] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 5 }, 1, new[] { (byte)'B' }); consensusContext.ChangeViewPayloads[2] = null; @@ -561,7 +601,7 @@ public void TestSerializeAndDeserializeConsensusContext() consensusContext.ChangeViewPayloads[5] = null; consensusContext.ChangeViewPayloads[6] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 1 }, 6, new[] { (byte)'D' }); - consensusContext.LastChangeViewPayloads = new ConsensusPayload[consensusContext.Validators.Length]; + consensusContext.LastChangeViewPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; var copiedContext = TestUtils.CopyMsgBySerialization(consensusContext, new ConsensusContext(null, null)); @@ -727,6 +767,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR }, PrepareRequestMessage = new PrepareRequest { + PrevHash = UInt256.Zero, TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })), @@ -780,6 +821,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC ChangeViewMessages = new Dictionary(), PrepareRequestMessage = new PrepareRequest { + PrevHash = UInt256.Zero, TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationMessages = new Dictionary() @@ -840,6 +882,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm ChangeViewMessages = new Dictionary(), PrepareRequestMessage = new PrepareRequest { + PrevHash = UInt256.Zero, TransactionHashes = txs.Select(p => p.Hash).ToArray() }, PreparationMessages = new Dictionary() @@ -909,15 +952,17 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm copiedMsg.CommitMessages.Should().BeEquivalentTo(msg.CommitMessages); } - private static ConsensusPayload MakeSignedPayload(ConsensusContext context, ConsensusMessage message, byte validatorIndex, byte[] witnessInvocationScript) + private static ExtensiblePayload MakeSignedPayload(ConsensusContext context, ConsensusMessage message, byte validatorIndex, byte[] witnessInvocationScript) { - return new ConsensusPayload + message.BlockIndex = context.Block.Index; + message.ValidatorIndex = validatorIndex; + return new ExtensiblePayload { - Version = context.Block.Version, - PrevHash = context.Block.PrevHash, - BlockIndex = context.Block.Index, - ValidatorIndex = validatorIndex, - ConsensusMessage = message, + Category = "Consensus", + ValidBlockStart = 0, + ValidBlockEnd = message.BlockIndex, + Sender = context.GetSender(validatorIndex), + Data = message.ToArray(), Witness = new Witness { InvocationScript = witnessInvocationScript, @@ -937,7 +982,7 @@ private StorageKey CreateStorageKeyForNativeNeo(byte prefix) return storageKey; } - private void TellConsensusPayload(IActorRef actor, ConsensusPayload payload) + private void TellConsensusPayload(IActorRef actor, ExtensiblePayload payload) { actor.Tell(new Blockchain.RelayResult { diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs index 10984867b8..297ad0e17c 100644 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs +++ b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs @@ -12,7 +12,6 @@ namespace Neo.UnitTests.Consensus { - [TestClass] public class UT_ConsensusContext : TestKit { diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs index bde5ed0106..9de9617e65 100644 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs +++ b/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs @@ -34,7 +34,7 @@ public void TestSetup() public void ConsensusServiceMailbox_Test_IsHighPriority() { // high priority - uut.IsHighPriority(new ConsensusPayload()).Should().Be(true); + uut.IsHighPriority(new ExtensiblePayload()).Should().Be(true); uut.IsHighPriority(new ConsensusService.SetViewNumber()).Should().Be(true); uut.IsHighPriority(new ConsensusService.Timer()).Should().Be(true); uut.IsHighPriority(new Blockchain.PersistCompleted()).Should().Be(true); diff --git a/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs b/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs index de89ba2fb2..3c66f59533 100644 --- a/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs +++ b/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs @@ -12,7 +12,7 @@ public class UT_RecoveryRequest public void Size_Get() { var test = new RecoveryRequest() { Timestamp = 1, ViewNumber = 1 }; - test.Size.Should().Be(10); + test.Size.Should().Be(15); } [TestMethod] diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs index 5ae7aec0da..a5174d6cfe 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs @@ -57,7 +57,7 @@ public void RemoteNode_Test_IsHighPriority() uut.IsHighPriority(Message.Create(MessageCommand.NotFound, s)).Should().Be(false); uut.IsHighPriority(Message.Create(MessageCommand.Transaction, s)).Should().Be(false); uut.IsHighPriority(Message.Create(MessageCommand.Block, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.Consensus, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.Extensible, s)).Should().Be(true); uut.IsHighPriority(Message.Create(MessageCommand.Reject, s)).Should().Be(false); //SPV protocol @@ -153,7 +153,7 @@ public void ProtocolHandlerMailbox_Test_ShallDrop() uut.ShallDrop(msg, emptyQueue).Should().Be(false); uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Consensus (no drop) - msg = Message.Create(MessageCommand.Consensus, s); + msg = Message.Create(MessageCommand.Extensible, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Reject (no drop) diff --git a/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs index fca280d1eb..755bf979c1 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs @@ -43,7 +43,7 @@ public void TaskManager_Test_IsHighPriority() // high priority // -> NewTasks: payload Block or Consensus uut.IsHighPriority(new TaskManager.NewTasks { Payload = new InvPayload { Type = InventoryType.Block } }).Should().Be(true); - uut.IsHighPriority(new TaskManager.NewTasks { Payload = new InvPayload { Type = InventoryType.Consensus } }).Should().Be(true); + uut.IsHighPriority(new TaskManager.NewTasks { Payload = new InvPayload { Type = InventoryType.Extensible } }).Should().Be(true); // any random object should not have priority object obj = null; diff --git a/tests/neo.UnitTests/Plugins/UT_Plugin.cs b/tests/neo.UnitTests/Plugins/UT_Plugin.cs index de32a45898..2d520c0740 100644 --- a/tests/neo.UnitTests/Plugins/UT_Plugin.cs +++ b/tests/neo.UnitTests/Plugins/UT_Plugin.cs @@ -21,8 +21,6 @@ private class dummyPersistencePlugin : IPersistencePlugin { } public void TestIP2PPlugin() { var pp = new DummyP2PPlugin() as IP2PPlugin; - - Assert.IsTrue(pp.OnConsensusMessage(null)); Assert.IsTrue(pp.OnP2PMessage(null)); } diff --git a/tests/neo.UnitTests/TestBlockchain.cs b/tests/neo.UnitTests/TestBlockchain.cs index 786b2e7062..18d04454d3 100644 --- a/tests/neo.UnitTests/TestBlockchain.cs +++ b/tests/neo.UnitTests/TestBlockchain.cs @@ -1,11 +1,15 @@ using Neo.Ledger; using System; +using System.Collections.Immutable; +using System.Linq; +using System.Reflection; namespace Neo.UnitTests { public static class TestBlockchain { public static readonly NeoSystem TheNeoSystem; + public static readonly UInt160[] DefaultExtensibleWitnessWhiteList; static TestBlockchain() { @@ -14,11 +18,21 @@ static TestBlockchain() // Ensure that blockchain is loaded - var _ = Blockchain.Singleton; + var bc = Blockchain.Singleton; + + DefaultExtensibleWitnessWhiteList = (typeof(Blockchain).GetField("extensibleWitnessWhiteList", + BindingFlags.Instance | BindingFlags.NonPublic).GetValue(bc) as ImmutableHashSet).ToArray(); + AddWhiteList(DefaultExtensibleWitnessWhiteList); // Add other address } - public static void InitializeMockNeoSystem() + public static void InitializeMockNeoSystem() { } + + public static void AddWhiteList(params UInt160[] address) { + var builder = ImmutableHashSet.CreateBuilder(); + foreach (var entry in address) builder.Add(entry); + + typeof(Blockchain).GetField("extensibleWitnessWhiteList", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(Blockchain.Singleton, builder.ToImmutable()); } } } From 3d640806ffe04ec634a2025abe5a7d966fa47d70 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 10 Jan 2021 22:29:09 +0800 Subject: [PATCH 27/65] Remove consensus (#2212) --- src/neo/Consensus/ChangeView.cs | 44 - src/neo/Consensus/ChangeViewReason.cs | 12 - src/neo/Consensus/Commit.cs | 26 - src/neo/Consensus/ConsensusContext.cs | 544 ---------- src/neo/Consensus/ConsensusMessage.cs | 55 - src/neo/Consensus/ConsensusMessageType.cs | 22 - src/neo/Consensus/ConsensusService.cs | 694 ------------ src/neo/Consensus/PrepareRequest.cs | 51 - src/neo/Consensus/PrepareResponse.cs | 29 - ...ecoveryMessage.ChangeViewPayloadCompact.cs | 41 - .../RecoveryMessage.CommitPayloadCompact.cs | 41 - ...coveryMessage.PreparationPayloadCompact.cs | 33 - src/neo/Consensus/RecoveryMessage.cs | 111 -- src/neo/Consensus/RecoveryRequest.cs | 31 - src/neo/NeoSystem.cs | 14 +- src/neo/Network/P2P/LocalNode.cs | 4 +- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 11 +- src/neo/Network/P2P/TaskManager.cs | 8 +- src/neo/Persistence/Prefixes.cs | 5 - src/neo/Plugins/IConsensusProvider.cs | 9 + src/neo/Plugins/IP2PPlugin.cs | 2 +- src/neo/SmartContract/Native/NeoToken.cs | 2 +- .../Consensus/UT_ChangeViewPayloadCompact.cs | 33 - tests/neo.UnitTests/Consensus/UT_Consensus.cs | 994 ------------------ .../Consensus/UT_ConsensusContext.cs | 193 ---- .../Consensus/UT_ConsensusServiceMailbox.cs | 47 - .../Consensus/UT_RecoveryRequest.cs | 29 - tests/neo.UnitTests/UT_NeoSystem.cs | 3 - 28 files changed, 22 insertions(+), 3066 deletions(-) delete mode 100644 src/neo/Consensus/ChangeView.cs delete mode 100644 src/neo/Consensus/ChangeViewReason.cs delete mode 100644 src/neo/Consensus/Commit.cs delete mode 100644 src/neo/Consensus/ConsensusContext.cs delete mode 100644 src/neo/Consensus/ConsensusMessage.cs delete mode 100644 src/neo/Consensus/ConsensusMessageType.cs delete mode 100644 src/neo/Consensus/ConsensusService.cs delete mode 100644 src/neo/Consensus/PrepareRequest.cs delete mode 100644 src/neo/Consensus/PrepareResponse.cs delete mode 100644 src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs delete mode 100644 src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs delete mode 100644 src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs delete mode 100644 src/neo/Consensus/RecoveryMessage.cs delete mode 100644 src/neo/Consensus/RecoveryRequest.cs create mode 100644 src/neo/Plugins/IConsensusProvider.cs delete mode 100644 tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs delete mode 100644 tests/neo.UnitTests/Consensus/UT_Consensus.cs delete mode 100644 tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs delete mode 100644 tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs delete mode 100644 tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs diff --git a/src/neo/Consensus/ChangeView.cs b/src/neo/Consensus/ChangeView.cs deleted file mode 100644 index 607e44cfd5..0000000000 --- a/src/neo/Consensus/ChangeView.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.IO; - -namespace Neo.Consensus -{ - public class ChangeView : ConsensusMessage - { - /// - /// NewViewNumber is always set to the current ViewNumber asking changeview + 1 - /// - public byte NewViewNumber => (byte)(ViewNumber + 1); - - /// - /// Timestamp of when the ChangeView message was created. This allows receiving nodes to ensure - /// they only respond once to a specific ChangeView request (it thus prevents replay of the ChangeView - /// message from repeatedly broadcasting RecoveryMessages). - /// - public ulong Timestamp; - - /// - /// Reason - /// - public ChangeViewReason Reason; - - public override int Size => base.Size + - sizeof(ulong) + // Timestamp - sizeof(ChangeViewReason); // Reason - - public ChangeView() : base(ConsensusMessageType.ChangeView) { } - - public override void Deserialize(BinaryReader reader) - { - base.Deserialize(reader); - Timestamp = reader.ReadUInt64(); - Reason = (ChangeViewReason)reader.ReadByte(); - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.Write(Timestamp); - writer.Write((byte)Reason); - } - } -} diff --git a/src/neo/Consensus/ChangeViewReason.cs b/src/neo/Consensus/ChangeViewReason.cs deleted file mode 100644 index 87c88c74c2..0000000000 --- a/src/neo/Consensus/ChangeViewReason.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Neo.Consensus -{ - public enum ChangeViewReason : byte - { - Timeout = 0x0, - ChangeAgreement = 0x1, - TxNotFound = 0x2, - TxRejectedByPolicy = 0x3, - TxInvalid = 0x4, - BlockRejectedByPolicy = 0x5 - } -} diff --git a/src/neo/Consensus/Commit.cs b/src/neo/Consensus/Commit.cs deleted file mode 100644 index fce9b8caaf..0000000000 --- a/src/neo/Consensus/Commit.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Neo.IO; -using System.IO; - -namespace Neo.Consensus -{ - public class Commit : ConsensusMessage - { - public byte[] Signature; - - public override int Size => base.Size + Signature.Length; - - public Commit() : base(ConsensusMessageType.Commit) { } - - public override void Deserialize(BinaryReader reader) - { - base.Deserialize(reader); - Signature = reader.ReadFixedBytes(64); - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.Write(Signature); - } - } -} diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs deleted file mode 100644 index 3101c2d54d..0000000000 --- a/src/neo/Consensus/ConsensusContext.cs +++ /dev/null @@ -1,544 +0,0 @@ -using Neo.Cryptography; -using Neo.Cryptography.ECC; -using Neo.IO; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.Persistence; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.CompilerServices; -using static Neo.Consensus.RecoveryMessage; - -namespace Neo.Consensus -{ - public class ConsensusContext : IDisposable, ISerializable - { - /// - /// Key for saving consensus state. - /// - private const byte ConsensusStatePrefix = 0xf4; - - public Block Block; - public byte ViewNumber; - public ECPoint[] Validators; - public int MyIndex; - public UInt256[] TransactionHashes; - public Dictionary Transactions; - public ExtensiblePayload[] PreparationPayloads; - public ExtensiblePayload[] CommitPayloads; - public ExtensiblePayload[] ChangeViewPayloads; - public ExtensiblePayload[] LastChangeViewPayloads; - // LastSeenMessage array stores the height of the last seen message, for each validator. - // if this node never heard from validator i, LastSeenMessage[i] will be -1. - public Dictionary LastSeenMessage { get; private set; } - - /// - /// Store all verified unsorted transactions' senders' fee currently in the consensus context. - /// - public TransactionVerificationContext VerificationContext = new TransactionVerificationContext(); - - public SnapshotView Snapshot { get; private set; } - private KeyPair keyPair; - private int _witnessSize; - private readonly Wallet wallet; - private readonly IStore store; - private Dictionary cachedMessages; - - public int F => (Validators.Length - 1) / 3; - public int M => Validators.Length - F; - public bool IsPrimary => MyIndex == Block.ConsensusData.PrimaryIndex; - public bool IsBackup => MyIndex >= 0 && MyIndex != Block.ConsensusData.PrimaryIndex; - public bool WatchOnly => MyIndex < 0; - public Header PrevHeader => Snapshot.GetHeader(Block.PrevHash); - public int CountCommitted => CommitPayloads.Count(p => p != null); - public int CountFailed - { - get - { - if (LastSeenMessage == null) return 0; - return Validators.Count(p => !LastSeenMessage.TryGetValue(p, out var value) || value < (Block.Index - 1)); - } - } - public bool ValidatorsChanged - { - get - { - if (Snapshot.Height == 0) return false; - TrimmedBlock currentBlock = Snapshot.Blocks[Snapshot.CurrentBlockHash]; - TrimmedBlock previousBlock = Snapshot.Blocks[currentBlock.PrevHash]; - return currentBlock.NextConsensus != previousBlock.NextConsensus; - } - } - - #region Consensus States - public bool RequestSentOrReceived => PreparationPayloads[Block.ConsensusData.PrimaryIndex] != null; - public bool ResponseSent => !WatchOnly && PreparationPayloads[MyIndex] != null; - public bool CommitSent => !WatchOnly && CommitPayloads[MyIndex] != null; - public bool BlockSent => Block.Transactions != null; - public bool ViewChanging => !WatchOnly && GetMessage(ChangeViewPayloads[MyIndex])?.NewViewNumber > ViewNumber; - public bool NotAcceptingPayloadsDueToViewChanging => ViewChanging && !MoreThanFNodesCommittedOrLost; - // A possible attack can happen if the last node to commit is malicious and either sends change view after his - // commit to stall nodes in a higher view, or if he refuses to send recovery messages. In addition, if a node - // asking change views loses network or crashes and comes back when nodes are committed in more than one higher - // numbered view, it is possible for the node accepting recovery to commit in any of the higher views, thus - // potentially splitting nodes among views and stalling the network. - public bool MoreThanFNodesCommittedOrLost => (CountCommitted + CountFailed) > F; - #endregion - - public int Size => throw new NotImplementedException(); - - public ConsensusContext(Wallet wallet, IStore store) - { - this.wallet = wallet; - this.store = store; - } - - public Block CreateBlock() - { - EnsureHeader(); - Contract contract = Contract.CreateMultiSigContract(M, Validators); - ContractParametersContext sc = new ContractParametersContext(Block); - for (int i = 0, j = 0; i < Validators.Length && j < M; i++) - { - if (GetMessage(CommitPayloads[i])?.ViewNumber != ViewNumber) continue; - sc.AddSignature(contract, Validators[i], GetMessage(CommitPayloads[i]).Signature); - j++; - } - Block.Witness = sc.GetWitnesses()[0]; - Block.Transactions = TransactionHashes.Select(p => Transactions[p]).ToArray(); - return Block; - } - - public ExtensiblePayload CreatePayload(ConsensusMessage message, byte[] invocationScript = null) - { - ExtensiblePayload payload = new ExtensiblePayload - { - Category = "Consensus", - ValidBlockStart = 0, - ValidBlockEnd = message.BlockIndex, - Sender = GetSender(message.ValidatorIndex), - Data = message.ToArray(), - Witness = invocationScript is null ? null : new Witness - { - InvocationScript = invocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(Validators[message.ValidatorIndex]) - } - }; - cachedMessages.TryAdd(payload.Hash, message); - return payload; - } - - public void Deserialize(BinaryReader reader) - { - Reset(0); - if (reader.ReadUInt32() != Block.Version) throw new FormatException(); - if (reader.ReadUInt32() != Block.Index) throw new InvalidOperationException(); - Block.Timestamp = reader.ReadUInt64(); - Block.NextConsensus = reader.ReadSerializable(); - if (Block.NextConsensus.Equals(UInt160.Zero)) - Block.NextConsensus = null; - Block.ConsensusData = reader.ReadSerializable(); - ViewNumber = reader.ReadByte(); - TransactionHashes = reader.ReadSerializableArray(); - Transaction[] transactions = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - PreparationPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - CommitPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - ChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - LastChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.ValidatorsCount); - if (TransactionHashes.Length == 0 && !RequestSentOrReceived) - TransactionHashes = null; - Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); - VerificationContext = new TransactionVerificationContext(); - if (Transactions != null) - { - foreach (Transaction tx in Transactions.Values) - VerificationContext.AddTransaction(tx); - } - } - - public void Dispose() - { - Snapshot?.Dispose(); - } - - public Block EnsureHeader() - { - if (TransactionHashes == null) return null; - if (Block.MerkleRoot is null) - Block.MerkleRoot = Block.CalculateMerkleRoot(Block.ConsensusData.Hash, TransactionHashes); - return Block; - } - - public ConsensusMessage GetMessage(ExtensiblePayload payload) - { - if (payload is null) return null; - if (!cachedMessages.TryGetValue(payload.Hash, out ConsensusMessage message)) - cachedMessages.Add(payload.Hash, message = ConsensusMessage.DeserializeFrom(payload.Data)); - return message; - } - - public T GetMessage(ExtensiblePayload payload) where T : ConsensusMessage - { - return (T)GetMessage(payload); - } - - private ChangeViewPayloadCompact GetChangeViewPayloadCompact(ExtensiblePayload payload) - { - ChangeView message = GetMessage(payload); - return new ChangeViewPayloadCompact - { - ValidatorIndex = message.ValidatorIndex, - OriginalViewNumber = message.ViewNumber, - Timestamp = message.Timestamp, - InvocationScript = payload.Witness.InvocationScript - }; - } - - private CommitPayloadCompact GetCommitPayloadCompact(ExtensiblePayload payload) - { - Commit message = GetMessage(payload); - return new CommitPayloadCompact - { - ViewNumber = message.ViewNumber, - ValidatorIndex = message.ValidatorIndex, - Signature = message.Signature, - InvocationScript = payload.Witness.InvocationScript - }; - } - - private PreparationPayloadCompact GetPreparationPayloadCompact(ExtensiblePayload payload) - { - return new PreparationPayloadCompact - { - ValidatorIndex = GetMessage(payload).ValidatorIndex, - InvocationScript = payload.Witness.InvocationScript - }; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte GetPrimaryIndex(byte viewNumber) - { - int p = ((int)Block.Index - viewNumber) % Validators.Length; - return p >= 0 ? (byte)p : (byte)(p + Validators.Length); - } - - public UInt160 GetSender(int index) - { - return Contract.CreateSignatureRedeemScript(Validators[index]).ToScriptHash(); - } - - public bool Load() - { - byte[] data = store.TryGet(ConsensusStatePrefix, null); - if (data is null || data.Length == 0) return false; - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader reader = new BinaryReader(ms)) - { - try - { - Deserialize(reader); - } - catch - { - return false; - } - return true; - } - } - - public ExtensiblePayload MakeChangeView(ChangeViewReason reason) - { - return ChangeViewPayloads[MyIndex] = MakeSignedPayload(new ChangeView - { - Reason = reason, - Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS() - }); - } - - public ExtensiblePayload MakeCommit() - { - return CommitPayloads[MyIndex] ?? (CommitPayloads[MyIndex] = MakeSignedPayload(new Commit - { - Signature = EnsureHeader().Sign(keyPair) - })); - } - - private ExtensiblePayload MakeSignedPayload(ConsensusMessage message) - { - message.BlockIndex = Block.Index; - message.ValidatorIndex = (byte)MyIndex; - message.ViewNumber = ViewNumber; - ExtensiblePayload payload = CreatePayload(message, null); - SignPayload(payload); - return payload; - } - - private void SignPayload(ExtensiblePayload payload) - { - ContractParametersContext sc; - try - { - sc = new ContractParametersContext(payload); - wallet.Sign(sc); - } - catch (InvalidOperationException) - { - return; - } - payload.Witness = sc.GetWitnesses()[0]; - } - - /// - /// Return the expected block size - /// - internal int GetExpectedBlockSize() - { - return GetExpectedBlockSizeWithoutTransactions(Transactions.Count) + // Base size - Transactions.Values.Sum(u => u.Size); // Sum Txs - } - - /// - /// Return the expected block system fee - /// - internal long GetExpectedBlockSystemFee() - { - return Transactions.Values.Sum(u => u.SystemFee); // Sum Txs - } - - /// - /// Return the expected block size without txs - /// - /// Expected transactions - internal int GetExpectedBlockSizeWithoutTransactions(int expectedTransactions) - { - var blockSize = - // BlockBase - sizeof(uint) + //Version - UInt256.Length + //PrevHash - UInt256.Length + //MerkleRoot - sizeof(ulong) + //Timestamp - sizeof(uint) + //Index - UInt160.Length + //NextConsensus - 1 + // - _witnessSize; //Witness - - blockSize += - // Block - Block.ConsensusData.Size + //ConsensusData - IO.Helper.GetVarSize(expectedTransactions + 1); //Transactions count - - return blockSize; - } - - /// - /// Prevent that block exceed the max size - /// - /// Ordered transactions - internal void EnsureMaxBlockLimitation(IEnumerable txs) - { - uint maxBlockSize = NativeContract.Policy.GetMaxBlockSize(Snapshot); - long maxBlockSystemFee = NativeContract.Policy.GetMaxBlockSystemFee(Snapshot); - uint maxTransactionsPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(Snapshot); - - // Limit Speaker proposal to the limit `MaxTransactionsPerBlock` or all available transactions of the mempool - txs = txs.Take((int)maxTransactionsPerBlock); - List hashes = new List(); - Transactions = new Dictionary(); - VerificationContext = new TransactionVerificationContext(); - - // Expected block size - var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count()); - var blockSystemFee = 0L; - - // Iterate transaction until reach the size or maximum system fee - foreach (Transaction tx in txs) - { - // Check if maximum block size has been already exceeded with the current selected set - blockSize += tx.Size; - if (blockSize > maxBlockSize) break; - - // Check if maximum block system fee has been already exceeded with the current selected set - blockSystemFee += tx.SystemFee; - if (blockSystemFee > maxBlockSystemFee) break; - - hashes.Add(tx.Hash); - Transactions.Add(tx.Hash, tx); - VerificationContext.AddTransaction(tx); - } - - TransactionHashes = hashes.ToArray(); - } - - public ExtensiblePayload MakePrepareRequest() - { - var random = new Random(); - Span buffer = stackalloc byte[sizeof(ulong)]; - random.NextBytes(buffer); - Block.ConsensusData.Nonce = BitConverter.ToUInt64(buffer); - EnsureMaxBlockLimitation(Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions()); - Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1); - - return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareRequest - { - Version = Block.Version, - PrevHash = Block.PrevHash, - Timestamp = Block.Timestamp, - Nonce = Block.ConsensusData.Nonce, - TransactionHashes = TransactionHashes - }); - } - - public ExtensiblePayload MakeRecoveryRequest() - { - return MakeSignedPayload(new RecoveryRequest - { - Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS() - }); - } - - public ExtensiblePayload MakeRecoveryMessage() - { - PrepareRequest prepareRequestMessage = null; - if (TransactionHashes != null) - { - prepareRequestMessage = new PrepareRequest - { - Version = Block.Version, - PrevHash = Block.PrevHash, - ViewNumber = ViewNumber, - Timestamp = Block.Timestamp, - BlockIndex = Block.Index, - Nonce = Block.ConsensusData.Nonce, - TransactionHashes = TransactionHashes - }; - } - return MakeSignedPayload(new RecoveryMessage() - { - ChangeViewMessages = LastChangeViewPayloads.Where(p => p != null).Select(p => GetChangeViewPayloadCompact(p)).Take(M).ToDictionary(p => (int)p.ValidatorIndex), - PrepareRequestMessage = prepareRequestMessage, - // We only need a PreparationHash set if we don't have the PrepareRequest information. - PreparationHash = TransactionHashes == null ? PreparationPayloads.Where(p => p != null).GroupBy(p => GetMessage(p).PreparationHash, (k, g) => new { Hash = k, Count = g.Count() }).OrderByDescending(p => p.Count).Select(p => p.Hash).FirstOrDefault() : null, - PreparationMessages = PreparationPayloads.Where(p => p != null).Select(p => GetPreparationPayloadCompact(p)).ToDictionary(p => (int)p.ValidatorIndex), - CommitMessages = CommitSent - ? CommitPayloads.Where(p => p != null).Select(p => GetCommitPayloadCompact(p)).ToDictionary(p => (int)p.ValidatorIndex) - : new Dictionary() - }); - } - - public ExtensiblePayload MakePrepareResponse() - { - return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareResponse - { - PreparationHash = PreparationPayloads[Block.ConsensusData.PrimaryIndex].Hash - }); - } - - public void Reset(byte viewNumber) - { - if (viewNumber == 0) - { - Snapshot?.Dispose(); - Snapshot = Blockchain.Singleton.GetSnapshot(); - Block = new Block - { - PrevHash = Snapshot.CurrentBlockHash, - Index = Snapshot.Height + 1, - NextConsensus = Blockchain.GetConsensusAddress( - NativeContract.NEO.ShouldRefreshCommittee(Snapshot.Height + 1) ? - NativeContract.NEO.ComputeNextBlockValidators(Snapshot) : - NativeContract.NEO.GetNextBlockValidators(Snapshot)) - }; - var pv = Validators; - Validators = NativeContract.NEO.GetNextBlockValidators(Snapshot); - if (_witnessSize == 0 || (pv != null && pv.Length != Validators.Length)) - { - // Compute the expected size of the witness - using (ScriptBuilder sb = new ScriptBuilder()) - { - for (int x = 0; x < M; x++) - { - sb.EmitPush(new byte[64]); - } - _witnessSize = new Witness - { - InvocationScript = sb.ToArray(), - VerificationScript = Contract.CreateMultiSigRedeemScript(M, Validators) - }.Size; - } - } - MyIndex = -1; - ChangeViewPayloads = new ExtensiblePayload[Validators.Length]; - LastChangeViewPayloads = new ExtensiblePayload[Validators.Length]; - CommitPayloads = new ExtensiblePayload[Validators.Length]; - if (ValidatorsChanged || LastSeenMessage is null) - { - var previous_last_seen_message = LastSeenMessage; - LastSeenMessage = new Dictionary(); - foreach (var validator in Validators) - { - if (previous_last_seen_message != null && previous_last_seen_message.TryGetValue(validator, out var value)) - LastSeenMessage[validator] = value; - else - LastSeenMessage[validator] = Snapshot.Height; - } - } - keyPair = null; - for (int i = 0; i < Validators.Length; i++) - { - WalletAccount account = wallet?.GetAccount(Validators[i]); - if (account?.HasKey != true) continue; - MyIndex = i; - keyPair = account.GetKey(); - break; - } - cachedMessages = new Dictionary(); - } - else - { - for (int i = 0; i < LastChangeViewPayloads.Length; i++) - if (GetMessage(ChangeViewPayloads[i])?.NewViewNumber >= viewNumber) - LastChangeViewPayloads[i] = ChangeViewPayloads[i]; - else - LastChangeViewPayloads[i] = null; - } - ViewNumber = viewNumber; - Block.ConsensusData = new ConsensusData - { - PrimaryIndex = GetPrimaryIndex(viewNumber) - }; - Block.MerkleRoot = null; - Block.Timestamp = 0; - Block.Transactions = null; - TransactionHashes = null; - PreparationPayloads = new ExtensiblePayload[Validators.Length]; - if (MyIndex >= 0) LastSeenMessage[Validators[MyIndex]] = Block.Index; - } - - public void Save() - { - store.PutSync(ConsensusStatePrefix, null, this.ToArray()); - } - - public void Serialize(BinaryWriter writer) - { - writer.Write(Block.Version); - writer.Write(Block.Index); - writer.Write(Block.Timestamp); - writer.Write(Block.NextConsensus ?? UInt160.Zero); - writer.Write(Block.ConsensusData); - writer.Write(ViewNumber); - writer.Write(TransactionHashes ?? new UInt256[0]); - writer.Write(Transactions?.Values.ToArray() ?? new Transaction[0]); - writer.WriteNullableArray(PreparationPayloads); - writer.WriteNullableArray(CommitPayloads); - writer.WriteNullableArray(ChangeViewPayloads); - writer.WriteNullableArray(LastChangeViewPayloads); - } - } -} diff --git a/src/neo/Consensus/ConsensusMessage.cs b/src/neo/Consensus/ConsensusMessage.cs deleted file mode 100644 index fceae7073a..0000000000 --- a/src/neo/Consensus/ConsensusMessage.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using System; -using System.IO; - -namespace Neo.Consensus -{ - public abstract class ConsensusMessage : ISerializable - { - public readonly ConsensusMessageType Type; - public uint BlockIndex; - public byte ValidatorIndex; - public byte ViewNumber; - - public virtual int Size => - sizeof(ConsensusMessageType) + //Type - sizeof(uint) + //BlockIndex - sizeof(byte) + //ValidatorIndex - sizeof(byte); //ViewNumber - - protected ConsensusMessage(ConsensusMessageType type) - { - if (!Enum.IsDefined(typeof(ConsensusMessageType), type)) - throw new ArgumentOutOfRangeException(nameof(type)); - this.Type = type; - } - - public virtual void Deserialize(BinaryReader reader) - { - if (Type != (ConsensusMessageType)reader.ReadByte()) - throw new FormatException(); - BlockIndex = reader.ReadUInt32(); - ValidatorIndex = reader.ReadByte(); - if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount) - throw new FormatException(); - ViewNumber = reader.ReadByte(); - } - - public static ConsensusMessage DeserializeFrom(byte[] data) - { - ConsensusMessageType type = (ConsensusMessageType)data[0]; - ISerializable message = ReflectionCache.CreateSerializable(type, data); - if (message is null) throw new FormatException(); - return (ConsensusMessage)message; - } - - public virtual void Serialize(BinaryWriter writer) - { - writer.Write((byte)Type); - writer.Write(BlockIndex); - writer.Write(ValidatorIndex); - writer.Write(ViewNumber); - } - } -} diff --git a/src/neo/Consensus/ConsensusMessageType.cs b/src/neo/Consensus/ConsensusMessageType.cs deleted file mode 100644 index fe13207db1..0000000000 --- a/src/neo/Consensus/ConsensusMessageType.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Neo.IO.Caching; - -namespace Neo.Consensus -{ - public enum ConsensusMessageType : byte - { - [ReflectionCache(typeof(ChangeView))] - ChangeView = 0x00, - - [ReflectionCache(typeof(PrepareRequest))] - PrepareRequest = 0x20, - [ReflectionCache(typeof(PrepareResponse))] - PrepareResponse = 0x21, - [ReflectionCache(typeof(Commit))] - Commit = 0x30, - - [ReflectionCache(typeof(RecoveryRequest))] - RecoveryRequest = 0x40, - [ReflectionCache(typeof(RecoveryMessage))] - RecoveryMessage = 0x41, - } -} diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs deleted file mode 100644 index 3361315e06..0000000000 --- a/src/neo/Consensus/ConsensusService.cs +++ /dev/null @@ -1,694 +0,0 @@ -using Akka.Actor; -using Akka.Configuration; -using Neo.Cryptography; -using Neo.IO; -using Neo.IO.Actors; -using Neo.Ledger; -using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; -using Neo.Persistence; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.Wallets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Neo.Consensus -{ - public sealed class ConsensusService : UntypedActor - { - public class Start { public bool IgnoreRecoveryLogs; } - public class SetViewNumber { public byte ViewNumber; } - internal class Timer { public uint Height; public byte ViewNumber; } - - private readonly ConsensusContext context; - private readonly IActorRef localNode; - private readonly IActorRef taskManager; - private readonly IActorRef blockchain; - private ICancelable timer_token; - private DateTime block_received_time; - private uint block_received_index; - private bool started = false; - - /// - /// This will record the information from last scheduled timer - /// - private DateTime clock_started = TimeProvider.Current.UtcNow; - private TimeSpan expected_delay = TimeSpan.Zero; - - /// - /// This will be cleared every block (so it will not grow out of control, but is used to prevent repeatedly - /// responding to the same message. - /// - private readonly HashSet knownHashes = new HashSet(); - /// - /// This variable is only true during OnRecoveryMessageReceived - /// - private bool isRecovering = false; - - public ConsensusService(IActorRef localNode, IActorRef taskManager, IActorRef blockchain, IStore store, Wallet wallet) - : this(localNode, taskManager, blockchain, new ConsensusContext(wallet, store)) - { - } - - internal ConsensusService(IActorRef localNode, IActorRef taskManager, IActorRef blockchain, ConsensusContext context) - { - this.localNode = localNode; - this.taskManager = taskManager; - this.blockchain = blockchain; - this.context = context; - Context.System.EventStream.Subscribe(Self, typeof(Blockchain.PersistCompleted)); - Context.System.EventStream.Subscribe(Self, typeof(Blockchain.RelayResult)); - } - - private bool AddTransaction(Transaction tx, bool verify) - { - if (verify) - { - VerifyResult result = tx.Verify(context.Snapshot, context.VerificationContext); - if (result == VerifyResult.PolicyFail) - { - Log($"reject tx: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); - RequestChangeView(ChangeViewReason.TxRejectedByPolicy); - return false; - } - else if (result != VerifyResult.Succeed) - { - Log($"Invalid transaction: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); - RequestChangeView(ChangeViewReason.TxInvalid); - return false; - } - } - context.Transactions[tx.Hash] = tx; - context.VerificationContext.AddTransaction(tx); - return CheckPrepareResponse(); - } - - private bool CheckPrepareResponse() - { - if (context.TransactionHashes.Length == context.Transactions.Count) - { - // if we are the primary for this view, but acting as a backup because we recovered our own - // previously sent prepare request, then we don't want to send a prepare response. - if (context.IsPrimary || context.WatchOnly) return true; - - // Check maximum block size via Native Contract policy - if (context.GetExpectedBlockSize() > NativeContract.Policy.GetMaxBlockSize(context.Snapshot)) - { - Log($"rejected block: {context.Block.Index} The size exceed the policy", LogLevel.Warning); - RequestChangeView(ChangeViewReason.BlockRejectedByPolicy); - return false; - } - // Check maximum block system fee via Native Contract policy - if (context.GetExpectedBlockSystemFee() > NativeContract.Policy.GetMaxBlockSystemFee(context.Snapshot)) - { - Log($"rejected block: {context.Block.Index} The system fee exceed the policy", LogLevel.Warning); - RequestChangeView(ChangeViewReason.BlockRejectedByPolicy); - return false; - } - - // Timeout extension due to prepare response sent - // around 2*15/M=30.0/5 ~ 40% block time (for M=5) - ExtendTimerByFactor(2); - - Log($"send prepare response"); - localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareResponse() }); - CheckPreparations(); - } - return true; - } - - private void ChangeTimer(TimeSpan delay) - { - clock_started = TimeProvider.Current.UtcNow; - expected_delay = delay; - timer_token.CancelIfNotNull(); - timer_token = Context.System.Scheduler.ScheduleTellOnceCancelable(delay, Self, new Timer - { - Height = context.Block.Index, - ViewNumber = context.ViewNumber - }, ActorRefs.NoSender); - } - - private void CheckCommits() - { - if (context.CommitPayloads.Count(p => context.GetMessage(p)?.ViewNumber == context.ViewNumber) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) - { - block_received_index = context.Block.Index; - block_received_time = TimeProvider.Current.UtcNow; - Block block = context.CreateBlock(); - Log($"relay block: height={block.Index} hash={block.Hash} tx={block.Transactions.Length}"); - blockchain.Tell(block); - } - } - - private void CheckExpectedView(byte viewNumber) - { - if (context.ViewNumber >= viewNumber) return; - var messages = context.ChangeViewPayloads.Select(p => context.GetMessage(p)).ToArray(); - // if there are `M` change view payloads with NewViewNumber greater than viewNumber, then, it is safe to move - if (messages.Count(p => p != null && p.NewViewNumber >= viewNumber) >= context.M) - { - if (!context.WatchOnly) - { - ChangeView message = messages[context.MyIndex]; - // Communicate the network about my agreement to move to `viewNumber` - // if my last change view payload, `message`, has NewViewNumber lower than current view to change - if (message is null || message.NewViewNumber < viewNumber) - localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeChangeView(ChangeViewReason.ChangeAgreement) }); - } - InitializeConsensus(viewNumber); - } - } - - private void CheckPreparations() - { - if (context.PreparationPayloads.Count(p => p != null) >= context.M && context.TransactionHashes.All(p => context.Transactions.ContainsKey(p))) - { - ExtensiblePayload payload = context.MakeCommit(); - Log($"send commit"); - context.Save(); - localNode.Tell(new LocalNode.SendDirectly { Inventory = payload }); - // Set timer, so we will resend the commit in case of a networking issue - ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock)); - CheckCommits(); - } - } - - private void InitializeConsensus(byte viewNumber) - { - context.Reset(viewNumber); - if (viewNumber > 0) - Log($"changeview: view={viewNumber} primary={context.Validators[context.GetPrimaryIndex((byte)(viewNumber - 1u))]}", LogLevel.Warning); - Log($"initialize: height={context.Block.Index} view={viewNumber} index={context.MyIndex} role={(context.IsPrimary ? "Primary" : context.WatchOnly ? "WatchOnly" : "Backup")}"); - if (context.WatchOnly) return; - if (context.IsPrimary) - { - if (isRecovering) - { - ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (viewNumber + 1))); - } - else - { - TimeSpan span = Blockchain.TimePerBlock; - if (block_received_index + 1 == context.Block.Index) - { - var diff = TimeProvider.Current.UtcNow - block_received_time; - if (diff >= span) - span = TimeSpan.Zero; - else - span -= diff; - } - ChangeTimer(span); - } - } - else - { - ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (viewNumber + 1))); - } - } - - private void Log(string message, LogLevel level = LogLevel.Info) - { - Utility.Log(nameof(ConsensusService), level, message); - } - - private void OnChangeViewReceived(ExtensiblePayload payload, ChangeView message) - { - if (message.NewViewNumber <= context.ViewNumber) - OnRecoveryRequestReceived(payload, message); - - if (context.CommitSent) return; - - var expectedView = context.GetMessage(context.ChangeViewPayloads[message.ValidatorIndex])?.NewViewNumber ?? 0; - if (message.NewViewNumber <= expectedView) - return; - - Log($"{nameof(OnChangeViewReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex} nv={message.NewViewNumber} reason={message.Reason}"); - context.ChangeViewPayloads[message.ValidatorIndex] = payload; - CheckExpectedView(message.NewViewNumber); - } - - private void OnCommitReceived(ExtensiblePayload payload, Commit commit) - { - ref ExtensiblePayload existingCommitPayload = ref context.CommitPayloads[commit.ValidatorIndex]; - if (existingCommitPayload != null) - { - if (existingCommitPayload.Hash != payload.Hash) - Log($"{nameof(OnCommitReceived)}: different commit from validator! height={commit.BlockIndex} index={commit.ValidatorIndex} view={commit.ViewNumber} existingView={context.GetMessage(existingCommitPayload).ViewNumber}", LogLevel.Warning); - return; - } - - // Timeout extension: commit has been received with success - // around 4*15s/M=60.0s/5=12.0s ~ 80% block time (for M=5) - ExtendTimerByFactor(4); - - if (commit.ViewNumber == context.ViewNumber) - { - Log($"{nameof(OnCommitReceived)}: height={commit.BlockIndex} view={commit.ViewNumber} index={commit.ValidatorIndex} nc={context.CountCommitted} nf={context.CountFailed}"); - - byte[] hashData = context.EnsureHeader()?.GetHashData(); - if (hashData == null) - { - existingCommitPayload = payload; - } - else if (Crypto.VerifySignature(hashData, commit.Signature, context.Validators[commit.ValidatorIndex])) - { - existingCommitPayload = payload; - CheckCommits(); - } - return; - } - // Receiving commit from another view - Log($"{nameof(OnCommitReceived)}: record commit for different view={commit.ViewNumber} index={commit.ValidatorIndex} height={commit.BlockIndex}"); - existingCommitPayload = payload; - } - - // this function increases existing timer (never decreases) with a value proportional to `maxDelayInBlockTimes`*`Blockchain.MillisecondsPerBlock` - private void ExtendTimerByFactor(int maxDelayInBlockTimes) - { - TimeSpan nextDelay = expected_delay - (TimeProvider.Current.UtcNow - clock_started) + TimeSpan.FromMilliseconds(maxDelayInBlockTimes * Blockchain.MillisecondsPerBlock / (double)context.M); - if (!context.WatchOnly && !context.ViewChanging && !context.CommitSent && (nextDelay > TimeSpan.Zero)) - ChangeTimer(nextDelay); - } - - private void OnConsensusPayload(ExtensiblePayload payload) - { - if (context.BlockSent) return; - ConsensusMessage message; - try - { - message = context.GetMessage(payload); - } - catch (FormatException) - { - return; - } - catch (IOException) - { - return; - } - if (message.BlockIndex != context.Block.Index) - { - if (context.Block.Index < message.BlockIndex) - { - Log($"chain sync: expected={message.BlockIndex} current={context.Block.Index - 1} nodes={LocalNode.Singleton.ConnectedCount}", LogLevel.Warning); - } - return; - } - if (message.ValidatorIndex >= context.Validators.Length) return; - if (payload.Sender != Contract.CreateSignatureRedeemScript(context.Validators[message.ValidatorIndex]).ToScriptHash()) return; - context.LastSeenMessage[context.Validators[message.ValidatorIndex]] = message.BlockIndex; - switch (message) - { - case ChangeView view: - OnChangeViewReceived(payload, view); - break; - case PrepareRequest request: - OnPrepareRequestReceived(payload, request); - break; - case PrepareResponse response: - OnPrepareResponseReceived(payload, response); - break; - case Commit commit: - OnCommitReceived(payload, commit); - break; - case RecoveryRequest request: - OnRecoveryRequestReceived(payload, request); - break; - case RecoveryMessage recovery: - OnRecoveryMessageReceived(recovery); - break; - } - } - - private void OnPersistCompleted(Block block) - { - Log($"persist block: height={block.Index} hash={block.Hash} tx={block.Transactions.Length}"); - knownHashes.Clear(); - InitializeConsensus(0); - } - - private void OnRecoveryMessageReceived(RecoveryMessage message) - { - // isRecovering is always set to false again after OnRecoveryMessageReceived - isRecovering = true; - int validChangeViews = 0, totalChangeViews = 0, validPrepReq = 0, totalPrepReq = 0; - int validPrepResponses = 0, totalPrepResponses = 0, validCommits = 0, totalCommits = 0; - - Log($"{nameof(OnRecoveryMessageReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex}"); - try - { - if (message.ViewNumber > context.ViewNumber) - { - if (context.CommitSent) return; - ExtensiblePayload[] changeViewPayloads = message.GetChangeViewPayloads(context); - totalChangeViews = changeViewPayloads.Length; - foreach (ExtensiblePayload changeViewPayload in changeViewPayloads) - if (ReverifyAndProcessPayload(changeViewPayload)) validChangeViews++; - } - if (message.ViewNumber == context.ViewNumber && !context.NotAcceptingPayloadsDueToViewChanging && !context.CommitSent) - { - if (!context.RequestSentOrReceived) - { - ExtensiblePayload prepareRequestPayload = message.GetPrepareRequestPayload(context); - if (prepareRequestPayload != null) - { - totalPrepReq = 1; - if (ReverifyAndProcessPayload(prepareRequestPayload)) validPrepReq++; - } - else if (context.IsPrimary) - SendPrepareRequest(); - } - ExtensiblePayload[] prepareResponsePayloads = message.GetPrepareResponsePayloads(context); - totalPrepResponses = prepareResponsePayloads.Length; - foreach (ExtensiblePayload prepareResponsePayload in prepareResponsePayloads) - if (ReverifyAndProcessPayload(prepareResponsePayload)) validPrepResponses++; - } - if (message.ViewNumber <= context.ViewNumber) - { - // Ensure we know about all commits from lower view numbers. - ExtensiblePayload[] commitPayloads = message.GetCommitPayloadsFromRecoveryMessage(context); - totalCommits = commitPayloads.Length; - foreach (ExtensiblePayload commitPayload in commitPayloads) - if (ReverifyAndProcessPayload(commitPayload)) validCommits++; - } - } - finally - { - Log($"{nameof(OnRecoveryMessageReceived)}: finished (valid/total) " + - $"ChgView: {validChangeViews}/{totalChangeViews} " + - $"PrepReq: {validPrepReq}/{totalPrepReq} " + - $"PrepResp: {validPrepResponses}/{totalPrepResponses} " + - $"Commits: {validCommits}/{totalCommits}"); - isRecovering = false; - } - } - - private void OnRecoveryRequestReceived(ExtensiblePayload payload, ConsensusMessage message) - { - // We keep track of the payload hashes received in this block, and don't respond with recovery - // in response to the same payload that we already responded to previously. - // ChangeView messages include a Timestamp when the change view is sent, thus if a node restarts - // and issues a change view for the same view, it will have a different hash and will correctly respond - // again; however replay attacks of the ChangeView message from arbitrary nodes will not trigger an - // additional recovery message response. - if (!knownHashes.Add(payload.Hash)) return; - - Log($"On{message.GetType().Name}Received: height={message.BlockIndex} index={message.ValidatorIndex} view={message.ViewNumber}"); - if (context.WatchOnly) return; - if (!context.CommitSent) - { - bool shouldSendRecovery = false; - int allowedRecoveryNodeCount = context.F; - // Limit recoveries to be sent from an upper limit of `f` nodes - for (int i = 1; i <= allowedRecoveryNodeCount; i++) - { - var chosenIndex = (message.ValidatorIndex + i) % context.Validators.Length; - if (chosenIndex != context.MyIndex) continue; - shouldSendRecovery = true; - break; - } - - if (!shouldSendRecovery) return; - } - Log($"send recovery: view={context.ViewNumber}"); - localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryMessage() }); - } - - private void OnPrepareRequestReceived(ExtensiblePayload payload, PrepareRequest message) - { - if (context.RequestSentOrReceived || context.NotAcceptingPayloadsDueToViewChanging) return; - if (message.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex || message.ViewNumber != context.ViewNumber) return; - if (message.Version != context.Block.Version || message.PrevHash != context.Block.PrevHash) return; - Log($"{nameof(OnPrepareRequestReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex} tx={message.TransactionHashes.Length}"); - if (message.Timestamp <= context.PrevHeader.Timestamp || message.Timestamp > TimeProvider.Current.UtcNow.AddMilliseconds(8 * Blockchain.MillisecondsPerBlock).ToTimestampMS()) - { - Log($"Timestamp incorrect: {message.Timestamp}", LogLevel.Warning); - return; - } - if (message.TransactionHashes.Any(p => context.Snapshot.ContainsTransaction(p))) - { - Log($"Invalid request: transaction already exists", LogLevel.Warning); - return; - } - - // Timeout extension: prepare request has been received with success - // around 2*15/M=30.0/5 ~ 40% block time (for M=5) - ExtendTimerByFactor(2); - - context.Block.Timestamp = message.Timestamp; - context.Block.ConsensusData.Nonce = message.Nonce; - context.TransactionHashes = message.TransactionHashes; - context.Transactions = new Dictionary(); - context.VerificationContext = new TransactionVerificationContext(); - for (int i = 0; i < context.PreparationPayloads.Length; i++) - if (context.PreparationPayloads[i] != null) - if (!context.GetMessage(context.PreparationPayloads[i]).PreparationHash.Equals(payload.Hash)) - context.PreparationPayloads[i] = null; - context.PreparationPayloads[message.ValidatorIndex] = payload; - byte[] hashData = context.EnsureHeader().GetHashData(); - for (int i = 0; i < context.CommitPayloads.Length; i++) - if (context.GetMessage(context.CommitPayloads[i])?.ViewNumber == context.ViewNumber) - if (!Crypto.VerifySignature(hashData, context.GetMessage(context.CommitPayloads[i]).Signature, context.Validators[i])) - context.CommitPayloads[i] = null; - - if (context.TransactionHashes.Length == 0) - { - // There are no tx so we should act like if all the transactions were filled - CheckPrepareResponse(); - return; - } - - Dictionary mempoolVerified = Blockchain.Singleton.MemPool.GetVerifiedTransactions().ToDictionary(p => p.Hash); - List unverified = new List(); - foreach (UInt256 hash in context.TransactionHashes) - { - if (mempoolVerified.TryGetValue(hash, out Transaction tx)) - { - if (!AddTransaction(tx, false)) - return; - } - else - { - if (Blockchain.Singleton.MemPool.TryGetValue(hash, out tx)) - unverified.Add(tx); - } - } - foreach (Transaction tx in unverified) - if (!AddTransaction(tx, true)) - return; - if (context.Transactions.Count < context.TransactionHashes.Length) - { - UInt256[] hashes = context.TransactionHashes.Where(i => !context.Transactions.ContainsKey(i)).ToArray(); - taskManager.Tell(new TaskManager.RestartTasks - { - Payload = InvPayload.Create(InventoryType.TX, hashes) - }); - } - } - - private void OnPrepareResponseReceived(ExtensiblePayload payload, PrepareResponse message) - { - if (message.ViewNumber != context.ViewNumber) return; - if (context.PreparationPayloads[message.ValidatorIndex] != null || context.NotAcceptingPayloadsDueToViewChanging) return; - if (context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex] != null && !message.PreparationHash.Equals(context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex].Hash)) - return; - - // Timeout extension: prepare response has been received with success - // around 2*15/M=30.0/5 ~ 40% block time (for M=5) - ExtendTimerByFactor(2); - - Log($"{nameof(OnPrepareResponseReceived)}: height={message.BlockIndex} view={message.ViewNumber} index={message.ValidatorIndex}"); - context.PreparationPayloads[message.ValidatorIndex] = payload; - if (context.WatchOnly || context.CommitSent) return; - if (context.RequestSentOrReceived) - CheckPreparations(); - } - - protected override void OnReceive(object message) - { - if (message is Start options) - { - if (started) return; - OnStart(options); - } - else - { - if (!started) return; - switch (message) - { - case SetViewNumber setView: - InitializeConsensus(setView.ViewNumber); - break; - case Timer timer: - OnTimer(timer); - break; - case Transaction transaction: - OnTransaction(transaction); - break; - case Blockchain.PersistCompleted completed: - OnPersistCompleted(completed.Block); - break; - case Blockchain.RelayResult rr: - if (rr.Result == VerifyResult.Succeed && rr.Inventory is ExtensiblePayload payload && payload.Category == "Consensus") - OnConsensusPayload(payload); - break; - } - } - } - - private void RequestRecovery() - { - if (context.Block.Index == Blockchain.Singleton.HeaderHeight + 1) - localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryRequest() }); - } - - private void OnStart(Start options) - { - Log("OnStart"); - started = true; - if (!options.IgnoreRecoveryLogs && context.Load()) - { - if (context.Transactions != null) - { - Sender.Ask(new Blockchain.FillMemoryPool - { - Transactions = context.Transactions.Values - }).Wait(); - } - if (context.CommitSent) - { - CheckPreparations(); - return; - } - } - InitializeConsensus(0); - // Issue a ChangeView with NewViewNumber of 0 to request recovery messages on start-up. - if (!context.WatchOnly) - RequestRecovery(); - } - - private void OnTimer(Timer timer) - { - if (context.WatchOnly || context.BlockSent) return; - if (timer.Height != context.Block.Index || timer.ViewNumber != context.ViewNumber) return; - Log($"timeout: height={timer.Height} view={timer.ViewNumber}"); - if (context.IsPrimary && !context.RequestSentOrReceived) - { - SendPrepareRequest(); - } - else if ((context.IsPrimary && context.RequestSentOrReceived) || context.IsBackup) - { - if (context.CommitSent) - { - // Re-send commit periodically by sending recover message in case of a network issue. - Log($"send recovery to resend commit"); - localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeRecoveryMessage() }); - ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << 1)); - } - else - { - var reason = ChangeViewReason.Timeout; - - if (context.Block != null && context.TransactionHashes?.Length > context.Transactions?.Count) - { - reason = ChangeViewReason.TxNotFound; - } - - RequestChangeView(reason); - } - } - } - - private void OnTransaction(Transaction transaction) - { - if (!context.IsBackup || context.NotAcceptingPayloadsDueToViewChanging || !context.RequestSentOrReceived || context.ResponseSent || context.BlockSent) - return; - if (context.Transactions.ContainsKey(transaction.Hash)) return; - if (!context.TransactionHashes.Contains(transaction.Hash)) return; - AddTransaction(transaction, true); - } - - protected override void PostStop() - { - Log("OnStop"); - started = false; - Context.System.EventStream.Unsubscribe(Self); - context.Dispose(); - base.PostStop(); - } - - public static Props Props(IActorRef localNode, IActorRef taskManager, IActorRef blockchain, IStore store, Wallet wallet) - { - return Akka.Actor.Props.Create(() => new ConsensusService(localNode, taskManager, blockchain, store, wallet)).WithMailbox("consensus-service-mailbox"); - } - - private void RequestChangeView(ChangeViewReason reason) - { - if (context.WatchOnly) return; - // Request for next view is always one view more than the current context.ViewNumber - // Nodes will not contribute for changing to a view higher than (context.ViewNumber+1), unless they are recovered - // The latter may happen by nodes in higher views with, at least, `M` proofs - byte expectedView = context.ViewNumber; - expectedView++; - ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (expectedView + 1))); - if ((context.CountCommitted + context.CountFailed) > context.F) - { - Log($"skip requesting change view: height={context.Block.Index} view={context.ViewNumber} nv={expectedView} nc={context.CountCommitted} nf={context.CountFailed} reason={reason}"); - RequestRecovery(); - return; - } - Log($"request change view: height={context.Block.Index} view={context.ViewNumber} nv={expectedView} nc={context.CountCommitted} nf={context.CountFailed} reason={reason}"); - localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeChangeView(reason) }); - CheckExpectedView(expectedView); - } - - private bool ReverifyAndProcessPayload(ExtensiblePayload payload) - { - if (!payload.Verify(context.Snapshot)) return false; - OnConsensusPayload(payload); - return true; - } - - private void SendPrepareRequest() - { - Log($"send prepare request: height={context.Block.Index} view={context.ViewNumber}"); - localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakePrepareRequest() }); - - if (context.Validators.Length == 1) - CheckPreparations(); - - if (context.TransactionHashes.Length > 0) - { - foreach (InvPayload payload in InvPayload.CreateGroup(InventoryType.TX, context.TransactionHashes)) - localNode.Tell(Message.Create(MessageCommand.Inv, payload)); - } - ChangeTimer(TimeSpan.FromMilliseconds((Blockchain.MillisecondsPerBlock << (context.ViewNumber + 1)) - (context.ViewNumber == 0 ? Blockchain.MillisecondsPerBlock : 0))); - } - } - - internal class ConsensusServiceMailbox : PriorityMailbox - { - public ConsensusServiceMailbox(Akka.Actor.Settings settings, Config config) - : base(settings, config) - { - } - - internal protected override bool IsHighPriority(object message) - { - switch (message) - { - case ExtensiblePayload _: - case ConsensusService.SetViewNumber _: - case ConsensusService.Timer _: - case Blockchain.PersistCompleted _: - return true; - default: - return false; - } - } - } -} diff --git a/src/neo/Consensus/PrepareRequest.cs b/src/neo/Consensus/PrepareRequest.cs deleted file mode 100644 index 9e96ca4202..0000000000 --- a/src/neo/Consensus/PrepareRequest.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Neo.IO; -using Neo.Network.P2P.Payloads; -using System; -using System.IO; -using System.Linq; - -namespace Neo.Consensus -{ - public class PrepareRequest : ConsensusMessage - { - public uint Version; - public UInt256 PrevHash; - public ulong Timestamp; - public ulong Nonce; - public UInt256[] TransactionHashes; - - public override int Size => base.Size - + sizeof(uint) //Version - + UInt256.Length //PrevHash - + sizeof(ulong) //Timestamp - + sizeof(ulong) //Nonce - + TransactionHashes.GetVarSize(); //TransactionHashes - - public PrepareRequest() - : base(ConsensusMessageType.PrepareRequest) - { - } - - public override void Deserialize(BinaryReader reader) - { - base.Deserialize(reader); - Version = reader.ReadUInt32(); - PrevHash = reader.ReadSerializable(); - Timestamp = reader.ReadUInt64(); - Nonce = reader.ReadUInt64(); - TransactionHashes = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - if (TransactionHashes.Distinct().Count() != TransactionHashes.Length) - throw new FormatException(); - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.Write(Version); - writer.Write(PrevHash); - writer.Write(Timestamp); - writer.Write(Nonce); - writer.Write(TransactionHashes); - } - } -} diff --git a/src/neo/Consensus/PrepareResponse.cs b/src/neo/Consensus/PrepareResponse.cs deleted file mode 100644 index 7c2956ccc2..0000000000 --- a/src/neo/Consensus/PrepareResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Neo.IO; -using System.IO; - -namespace Neo.Consensus -{ - public class PrepareResponse : ConsensusMessage - { - public UInt256 PreparationHash; - - public override int Size => base.Size + PreparationHash.Size; - - public PrepareResponse() - : base(ConsensusMessageType.PrepareResponse) - { - } - - public override void Deserialize(BinaryReader reader) - { - base.Deserialize(reader); - PreparationHash = reader.ReadSerializable(); - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.Write(PreparationHash); - } - } -} diff --git a/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs deleted file mode 100644 index 8419193de6..0000000000 --- a/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Neo.IO; -using System; -using System.IO; - -namespace Neo.Consensus -{ - partial class RecoveryMessage - { - public class ChangeViewPayloadCompact : ISerializable - { - public byte ValidatorIndex; - public byte OriginalViewNumber; - public ulong Timestamp; - public byte[] InvocationScript; - - int ISerializable.Size => - sizeof(byte) + //ValidatorIndex - sizeof(byte) + //OriginalViewNumber - sizeof(ulong) + //Timestamp - InvocationScript.GetVarSize(); //InvocationScript - - void ISerializable.Deserialize(BinaryReader reader) - { - ValidatorIndex = reader.ReadByte(); - if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount) - throw new FormatException(); - OriginalViewNumber = reader.ReadByte(); - Timestamp = reader.ReadUInt64(); - InvocationScript = reader.ReadVarBytes(1024); - } - - void ISerializable.Serialize(BinaryWriter writer) - { - writer.Write(ValidatorIndex); - writer.Write(OriginalViewNumber); - writer.Write(Timestamp); - writer.WriteVarBytes(InvocationScript); - } - } - } -} diff --git a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs deleted file mode 100644 index 81b3bc37af..0000000000 --- a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Neo.IO; -using System; -using System.IO; - -namespace Neo.Consensus -{ - partial class RecoveryMessage - { - public class CommitPayloadCompact : ISerializable - { - public byte ViewNumber; - public byte ValidatorIndex; - public byte[] Signature; - public byte[] InvocationScript; - - int ISerializable.Size => - sizeof(byte) + //ViewNumber - sizeof(byte) + //ValidatorIndex - Signature.Length + //Signature - InvocationScript.GetVarSize(); //InvocationScript - - void ISerializable.Deserialize(BinaryReader reader) - { - ViewNumber = reader.ReadByte(); - ValidatorIndex = reader.ReadByte(); - if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount) - throw new FormatException(); - Signature = reader.ReadFixedBytes(64); - InvocationScript = reader.ReadVarBytes(1024); - } - - void ISerializable.Serialize(BinaryWriter writer) - { - writer.Write(ViewNumber); - writer.Write(ValidatorIndex); - writer.Write(Signature); - writer.WriteVarBytes(InvocationScript); - } - } - } -} diff --git a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs deleted file mode 100644 index 72fbd2de2f..0000000000 --- a/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Neo.IO; -using System; -using System.IO; - -namespace Neo.Consensus -{ - partial class RecoveryMessage - { - public class PreparationPayloadCompact : ISerializable - { - public byte ValidatorIndex; - public byte[] InvocationScript; - - int ISerializable.Size => - sizeof(byte) + //ValidatorIndex - InvocationScript.GetVarSize(); //InvocationScript - - void ISerializable.Deserialize(BinaryReader reader) - { - ValidatorIndex = reader.ReadByte(); - if (ValidatorIndex >= ProtocolSettings.Default.ValidatorsCount) - throw new FormatException(); - InvocationScript = reader.ReadVarBytes(1024); - } - - void ISerializable.Serialize(BinaryWriter writer) - { - writer.Write(ValidatorIndex); - writer.WriteVarBytes(InvocationScript); - } - } - } -} diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs deleted file mode 100644 index 11b8bd0361..0000000000 --- a/src/neo/Consensus/RecoveryMessage.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Neo.IO; -using Neo.Network.P2P.Payloads; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Neo.Consensus -{ - public partial class RecoveryMessage : ConsensusMessage - { - public Dictionary ChangeViewMessages; - public PrepareRequest PrepareRequestMessage; - /// The PreparationHash in case the PrepareRequest hasn't been received yet. - /// This can be null if the PrepareRequest information is present, since it can be derived in that case. - public UInt256 PreparationHash; - public Dictionary PreparationMessages; - public Dictionary CommitMessages; - - public override int Size => base.Size - + /* ChangeViewMessages */ ChangeViewMessages?.Values.GetVarSize() ?? 0 - + /* PrepareRequestMessage */ 1 + PrepareRequestMessage?.Size ?? 0 - + /* PreparationHash */ PreparationHash?.Size ?? 0 - + /* PreparationMessages */ PreparationMessages?.Values.GetVarSize() ?? 0 - + /* CommitMessages */ CommitMessages?.Values.GetVarSize() ?? 0; - - public RecoveryMessage() : base(ConsensusMessageType.RecoveryMessage) - { - } - - public override void Deserialize(BinaryReader reader) - { - base.Deserialize(reader); - ChangeViewMessages = reader.ReadSerializableArray(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); - if (reader.ReadBoolean()) - PrepareRequestMessage = reader.ReadSerializable(); - else - { - int preparationHashSize = UInt256.Zero.Size; - if (preparationHashSize == (int)reader.ReadVarInt((ulong)preparationHashSize)) - PreparationHash = new UInt256(reader.ReadFixedBytes(preparationHashSize)); - } - - PreparationMessages = reader.ReadSerializableArray(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); - CommitMessages = reader.ReadSerializableArray(ProtocolSettings.Default.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); - } - - internal ExtensiblePayload[] GetChangeViewPayloads(ConsensusContext context) - { - return ChangeViewMessages.Values.Select(p => context.CreatePayload(new ChangeView - { - BlockIndex = BlockIndex, - ValidatorIndex = p.ValidatorIndex, - ViewNumber = p.OriginalViewNumber, - Timestamp = p.Timestamp - }, p.InvocationScript)).ToArray(); - } - - internal ExtensiblePayload[] GetCommitPayloadsFromRecoveryMessage(ConsensusContext context) - { - return CommitMessages.Values.Select(p => context.CreatePayload(new Commit - { - BlockIndex = BlockIndex, - ValidatorIndex = p.ValidatorIndex, - ViewNumber = p.ViewNumber, - Signature = p.Signature - }, p.InvocationScript)).ToArray(); - } - - internal ExtensiblePayload GetPrepareRequestPayload(ConsensusContext context) - { - if (PrepareRequestMessage == null) return null; - if (!PreparationMessages.TryGetValue(context.Block.ConsensusData.PrimaryIndex, out PreparationPayloadCompact compact)) - return null; - return context.CreatePayload(PrepareRequestMessage, compact.InvocationScript); - } - - internal ExtensiblePayload[] GetPrepareResponsePayloads(ConsensusContext context) - { - UInt256 preparationHash = PreparationHash ?? context.PreparationPayloads[context.Block.ConsensusData.PrimaryIndex]?.Hash; - if (preparationHash is null) return Array.Empty(); - return PreparationMessages.Values.Where(p => p.ValidatorIndex != context.Block.ConsensusData.PrimaryIndex).Select(p => context.CreatePayload(new PrepareResponse - { - BlockIndex = BlockIndex, - ValidatorIndex = p.ValidatorIndex, - ViewNumber = ViewNumber, - PreparationHash = preparationHash - }, p.InvocationScript)).ToArray(); - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.Write(ChangeViewMessages.Values.ToArray()); - bool hasPrepareRequestMessage = PrepareRequestMessage != null; - writer.Write(hasPrepareRequestMessage); - if (hasPrepareRequestMessage) - writer.Write(PrepareRequestMessage); - else - { - if (PreparationHash == null) - writer.WriteVarInt(0); - else - writer.WriteVarBytes(PreparationHash.ToArray()); - } - - writer.Write(PreparationMessages.Values.ToArray()); - writer.Write(CommitMessages.Values.ToArray()); - } - } -} diff --git a/src/neo/Consensus/RecoveryRequest.cs b/src/neo/Consensus/RecoveryRequest.cs deleted file mode 100644 index 6ea0d7c2f7..0000000000 --- a/src/neo/Consensus/RecoveryRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.IO; - -namespace Neo.Consensus -{ - public class RecoveryRequest : ConsensusMessage - { - /// - /// Timestamp of when the ChangeView message was created. This allows receiving nodes to ensure - /// they only respond once to a specific RecoveryRequest request. - /// In this sense, it prevents replay of the RecoveryRequest message from the repeatedly broadcast of Recovery's messages. - /// - public ulong Timestamp; - - public override int Size => base.Size - + sizeof(ulong); //Timestamp - - public RecoveryRequest() : base(ConsensusMessageType.RecoveryRequest) { } - - public override void Deserialize(BinaryReader reader) - { - base.Deserialize(reader); - Timestamp = reader.ReadUInt64(); - } - - public override void Serialize(BinaryWriter writer) - { - base.Serialize(writer); - writer.Write(Timestamp); - } - } -} diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 76980ca319..9ba6eb202d 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -1,10 +1,8 @@ using Akka.Actor; -using Neo.Consensus; using Neo.Ledger; using Neo.Network.P2P; using Neo.Persistence; using Neo.Plugins; -using Neo.Wallets; using System; namespace Neo @@ -15,12 +13,10 @@ public class NeoSystem : IDisposable $"akka {{ log-dead-letters = off , loglevel = warning, loggers = [ \"{typeof(Utility.Logger).AssemblyQualifiedName}\" ] }}" + $"blockchain-mailbox {{ mailbox-type: \"{typeof(BlockchainMailbox).AssemblyQualifiedName}\" }}" + $"task-manager-mailbox {{ mailbox-type: \"{typeof(TaskManagerMailbox).AssemblyQualifiedName}\" }}" + - $"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}" + - $"consensus-service-mailbox {{ mailbox-type: \"{typeof(ConsensusServiceMailbox).AssemblyQualifiedName}\" }}"); + $"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}"); public IActorRef Blockchain { get; } public IActorRef LocalNode { get; } - internal IActorRef TaskManager { get; } - public IActorRef Consensus { get; private set; } + public IActorRef TaskManager { get; } private readonly string storage_engine; private readonly IStore store; @@ -86,12 +82,6 @@ internal void ResumeNodeStartup() } } - public void StartConsensus(Wallet wallet, IStore consensus_store = null, bool ignoreRecoveryLogs = false) - { - Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, this.Blockchain, consensus_store ?? store, wallet)); - Consensus.Tell(new ConsensusService.Start { IgnoreRecoveryLogs = ignoreRecoveryLogs }, Blockchain); - } - public void StartNode(ChannelsConfig config) { start_message = config; diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index c8bc937001..c2941c38bd 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -15,8 +15,8 @@ namespace Neo.Network.P2P { public class LocalNode : Peer { - internal class RelayDirectly { public IInventory Inventory; } - internal class SendDirectly { public IInventory Inventory; } + public class RelayDirectly { public IInventory Inventory; } + public class SendDirectly { public IInventory Inventory; } public const uint ProtocolVersion = 0; private const int MaxCountFromSeedList = 5; diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index b0b1051c71..7d85875c56 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -288,15 +288,10 @@ private void OnGetHeadersMessageReceived(GetBlockByIndexPayload payload) private void OnInventoryReceived(IInventory inventory) { pendingKnownHashes.Remove(inventory.Hash); - switch (inventory) + if (inventory is Block block) { - case Transaction transaction: - system.Consensus?.Tell(transaction); - break; - case Block block: - if (block.Index > Blockchain.Singleton.Height + InvPayload.MaxHashesCount) return; - UpdateLastBlockIndex(block.Index, false); - break; + if (block.Index > Blockchain.Singleton.Height + InvPayload.MaxHashesCount) return; + UpdateLastBlockIndex(block.Index, false); } knownHashes.Add(inventory.Hash); system.TaskManager.Tell(inventory); diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 07088a9fb5..ef6e09e443 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -13,11 +13,11 @@ namespace Neo.Network.P2P { - internal class TaskManager : UntypedActor + public class TaskManager : UntypedActor { - public class Register { public VersionPayload Version; } - public class Update { public uint LastBlockIndex; public bool RequestTasks; } - public class NewTasks { public InvPayload Payload; } + internal class Register { public VersionPayload Version; } + internal class Update { public uint LastBlockIndex; public bool RequestTasks; } + internal class NewTasks { public InvPayload Payload; } public class RestartTasks { public InvPayload Payload; } private class Timer { } diff --git a/src/neo/Persistence/Prefixes.cs b/src/neo/Persistence/Prefixes.cs index fcd7549325..3b63665326 100644 --- a/src/neo/Persistence/Prefixes.cs +++ b/src/neo/Persistence/Prefixes.cs @@ -10,10 +10,5 @@ internal static class Prefixes public const byte IX_HeaderHashList = 0x80; public const byte IX_CurrentBlock = 0xc0; public const byte IX_CurrentHeader = 0xc1; - - /* Prefixes 0xf0 to 0xff are reserved for external use. - * - * Note: The saved consensus state uses the Prefix 0xf4 - */ } } diff --git a/src/neo/Plugins/IConsensusProvider.cs b/src/neo/Plugins/IConsensusProvider.cs new file mode 100644 index 0000000000..a7e021863a --- /dev/null +++ b/src/neo/Plugins/IConsensusProvider.cs @@ -0,0 +1,9 @@ +using Neo.Wallets; + +namespace Neo.Plugins +{ + public interface IConsensusProvider + { + void Start(Wallet wallet); + } +} diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs index 8323339d75..ea792e721c 100644 --- a/src/neo/Plugins/IP2PPlugin.cs +++ b/src/neo/Plugins/IP2PPlugin.cs @@ -6,6 +6,6 @@ namespace Neo.Plugins public interface IP2PPlugin { bool OnP2PMessage(Message message) => true; - void OnVerifiedInventory(IInventory inventory); + void OnVerifiedInventory(IInventory inventory) { } } } diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index 375cb628ef..64971dc640 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -303,7 +303,7 @@ private CachedCommittee GetCommitteeFromCache(StoreView snapshot) return snapshot.Storages[CreateStorageKey(Prefix_Committee)].GetInteroperable(); } - internal ECPoint[] ComputeNextBlockValidators(StoreView snapshot) + public ECPoint[] ComputeNextBlockValidators(StoreView snapshot) { return ComputeCommitteeMembers(snapshot).Select(p => p.PublicKey).Take(ProtocolSettings.Default.ValidatorsCount).OrderBy(p => p).ToArray(); } diff --git a/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs b/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs deleted file mode 100644 index 757d041801..0000000000 --- a/tests/neo.UnitTests/Consensus/UT_ChangeViewPayloadCompact.cs +++ /dev/null @@ -1,33 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Consensus; -using Neo.IO; - -namespace Neo.UnitTests.Consensus -{ - [TestClass] - public class UT_ChangeViewPayloadCompact - { - [TestMethod] - public void Size_Get() - { - var test = new RecoveryMessage.ChangeViewPayloadCompact() { Timestamp = 1, ValidatorIndex = 1, InvocationScript = new byte[0], OriginalViewNumber = 1 }; - ((ISerializable)test).Size.Should().Be(11); - - test = new RecoveryMessage.ChangeViewPayloadCompact() { Timestamp = 1, ValidatorIndex = 1, InvocationScript = new byte[1024], OriginalViewNumber = 1 }; - ((ISerializable)test).Size.Should().Be(1037); - } - - [TestMethod] - public void DeserializeAndSerialize() - { - var test = new RecoveryMessage.ChangeViewPayloadCompact() { Timestamp = 1, ValidatorIndex = 2, InvocationScript = new byte[] { 1, 2, 3 }, OriginalViewNumber = 3 }; - var clone = test.ToArray().AsSerializable(); - - Assert.AreEqual(test.Timestamp, clone.Timestamp); - Assert.AreEqual(test.ValidatorIndex, clone.ValidatorIndex); - Assert.AreEqual(test.OriginalViewNumber, clone.OriginalViewNumber); - CollectionAssert.AreEqual(test.InvocationScript, clone.InvocationScript); - } - } -} diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs deleted file mode 100644 index a900f90797..0000000000 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ /dev/null @@ -1,994 +0,0 @@ -using Akka.Actor; -using Akka.TestKit; -using Akka.TestKit.Xunit2; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Neo.Consensus; -using Neo.Cryptography; -using Neo.IO; -using Neo.Ledger; -using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.UnitTests.Cryptography; -using Neo.Wallets; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Reflection; -using System.Security.Cryptography; -using static Neo.SmartContract.Native.NeoToken; -using ECPoint = Neo.Cryptography.ECC.ECPoint; - -namespace Neo.UnitTests.Consensus -{ - [TestClass] - public class ConsensusTests : TestKit - { - private KeyPair[] kp_array; - - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - - var moked = new ECPoint[] { - ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", Neo.Cryptography.ECC.ECCurve.Secp256r1) - }; - - kp_array = new KeyPair[7] - { - UT_Crypto.generateKey(32), // not used, kept for index consistency, didactically - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32), - UT_Crypto.generateKey(32) - }.OrderBy(p => p.PublicKey).ToArray(); - - TestBlockchain.AddWhiteList(TestBlockchain.DefaultExtensibleWitnessWhiteList - .Concat(moked.Select(u => Contract.CreateSignatureContract(u).ScriptHash)) - .Concat(kp_array.Select(u => Contract.CreateSignatureContract(u.PublicKey).ScriptHash)) - .ToArray()); - } - - [TestCleanup] - public void Cleanup() - { - TestBlockchain.AddWhiteList(TestBlockchain.DefaultExtensibleWitnessWhiteList); - Shutdown(); - } - - [TestMethod] - public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Commits() - { - var mockWallet = new Mock(); - mockWallet.Setup(p => p.GetAccount(It.IsAny())).Returns(p => new TestWalletAccount(p)); - Console.WriteLine($"\n(UT-Consensus) Wallet is: {mockWallet.Object.GetAccount(UInt160.Zero).GetKey().PublicKey}"); - - var mockContext = new Mock(mockWallet.Object, Blockchain.Singleton.Store); - var timeValues = new[] { - new DateTime(1980, 06, 01, 0, 0, 1, 001, DateTimeKind.Utc), // For tests, used below - new DateTime(1980, 06, 01, 0, 0, 3, 001, DateTimeKind.Utc), // For receiving block - new DateTime(1980, 05, 01, 0, 0, 5, 001, DateTimeKind.Utc), // For Initialize - new DateTime(1980, 06, 01, 0, 0, 15, 001, DateTimeKind.Utc), // unused - }; - for (int i = 0; i < timeValues.Length; i++) - Console.WriteLine($"time {i}: {timeValues[i]} "); - ulong defaultTimestamp = 328665601001; // GMT: Sunday, June 1, 1980 12:00:01.001 AM - // check basic ConsensusContext - // mockConsensusContext.Object.block_received_time.ToTimestamp().Should().Be(4244941697); //1968-06-01 00:00:01 - // ============================================================================ - // creating ConsensusService actor - // ============================================================================ - - int timeIndex = 0; - var timeMock = new Mock(); - timeMock.SetupGet(tp => tp.UtcNow).Returns(() => timeValues[timeIndex]); - //.Callback(() => timeIndex = timeIndex + 1); //Comment while index is not fixed - - TimeProvider.Current = timeMock.Object; - TimeProvider.Current.UtcNow.ToTimestampMS().Should().Be(defaultTimestamp); //1980-06-01 00:00:15:001 - - //public void Log(string message, LogLevel level) - //create ILogPlugin for Tests - /* - mockConsensusContext.Setup(mr => mr.Log(It.IsAny(), It.IsAny())) - .Callback((string message, LogLevel level) => { - Console.WriteLine($"CONSENSUS LOG: {message}"); - } - ); - */ - - // Creating a test block - Header header = new Header(); - TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal); - header.Size.Should().Be(105); - Console.WriteLine($"header {header} hash {header.Hash} {header.PrevHash} timestamp {timestampVal}"); - timestampVal.Should().Be(defaultTimestamp); - TestProbe subscriber = CreateTestProbe(); - TestActorRef actorConsensus = ActorOfAsTestActorRef( - Akka.Actor.Props.Create(() => (ConsensusService)Activator.CreateInstance(typeof(ConsensusService), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { subscriber, subscriber, subscriber, mockContext.Object }, null)) - ); - - var testPersistCompleted = new Blockchain.PersistCompleted - { - Block = new Block - { - Version = header.Version, - PrevHash = header.PrevHash, - MerkleRoot = header.MerkleRoot, - Timestamp = header.Timestamp, - Index = header.Index, - NextConsensus = header.NextConsensus, - Transactions = new Transaction[0] - } - }; - Console.WriteLine("\n=========================="); - Console.WriteLine("Telling a new block to actor consensus..."); - Console.WriteLine("will trigger OnPersistCompleted without OnStart flag!"); - // OnPersist will not launch timer, we need OnStart - actorConsensus.Tell(testPersistCompleted); - Console.WriteLine("\n=========================="); - - Console.WriteLine("\n=========================="); - Console.WriteLine("will start consensus!"); - actorConsensus.Tell(new ConsensusService.Start - { - IgnoreRecoveryLogs = true - }); - - Console.WriteLine("Waiting for subscriber recovery message..."); - // The next line force a waits, then, subscriber keeps running its thread - // In the next case it waits for a Msg of type LocalNode.SendDirectly - // As we may expect, as soon as consensus start it sends a RecoveryRequest of this aforementioned type - var askingForInitialRecovery = subscriber.ExpectMsg(); - Console.WriteLine($"Recovery Message I: {askingForInitialRecovery}"); - foreach (var validator in mockContext.Object.Validators) - { - mockContext.Object.LastSeenMessage[validator] = 0; - } - // Ensuring cast of type ConsensusPayload from the received message from subscriber - ExtensiblePayload initialRecoveryPayload = (ExtensiblePayload)askingForInitialRecovery.Inventory; - // Ensuring casting of type RecoveryRequest - RecoveryRequest rrm = initialRecoveryPayload.Data.AsSerializable(); - rrm.Timestamp.Should().Be(defaultTimestamp); - - Console.WriteLine("Waiting for backup ChangeView... "); - var backupOnAskingChangeView = subscriber.ExpectMsg(); - var changeViewPayload = (ExtensiblePayload)backupOnAskingChangeView.Inventory; - ChangeView cvm = changeViewPayload.Data.AsSerializable(); - cvm.Timestamp.Should().Be(defaultTimestamp); - cvm.ViewNumber.Should().Be(0); - cvm.Reason.Should().Be(ChangeViewReason.Timeout); - - // Original Contract - Contract originalContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators); - Console.WriteLine($"\nORIGINAL Contract is: {originalContract.ScriptHash}"); - Console.WriteLine($"ORIGINAL NextConsensus: {mockContext.Object.Block.NextConsensus}\nENSURING values..."); - originalContract.ScriptHash.Should().Be(UInt160.Parse("0xe239c7228fa6b46cc0cf43623b2f934301d0b4f7")); - mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0xe239c7228fa6b46cc0cf43623b2f934301d0b4f7")); - - Console.WriteLine("\n=========================="); - Console.WriteLine("will trigger OnPersistCompleted again with OnStart flag!"); - actorConsensus.Tell(testPersistCompleted); - Console.WriteLine("\n=========================="); - - // Disabling flag ViewChanging by reverting cache of changeview that was sent - mockContext.Object.ChangeViewPayloads[mockContext.Object.MyIndex] = null; - Console.WriteLine("Forcing Failed nodes for recovery request... "); - mockContext.Object.CountFailed.Should().Be(0); - mockContext.Object.LastSeenMessage.Clear(); - mockContext.Object.CountFailed.Should().Be(7); - Console.WriteLine("\nWaiting for recovery due to failed nodes... "); - var backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); - var recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory; - rrm = recoveryPayload.Data.AsSerializable(); - rrm.Timestamp.Should().Be(defaultTimestamp); - - Console.WriteLine("will create template MakePrepareRequest..."); - mockContext.Object.PrevHeader.Timestamp = defaultTimestamp; - mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0xe239c7228fa6b46cc0cf43623b2f934301d0b4f7")); - var prepReq = mockContext.Object.MakePrepareRequest(); - var ppToSend = prepReq.Data.AsSerializable(); - // Forcing hashes to 0 because mempool is currently shared - ppToSend.TransactionHashes = new UInt256[0]; - ppToSend.TransactionHashes.Length.Should().Be(0); - Console.WriteLine($"\nAsserting PreparationPayloads is 1 (After MakePrepareRequest)..."); - mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(1); - mockContext.Object.PreparationPayloads[ppToSend.ValidatorIndex] = null; - - Console.WriteLine("will tell prepare request!"); - prepReq = new ExtensiblePayload - { - Category = "Consensus", - ValidBlockStart = 0, - ValidBlockEnd = ppToSend.BlockIndex, - Sender = prepReq.Sender, - Data = ppToSend.ToArray(), - Witness = prepReq.Witness - }; - TellConsensusPayload(actorConsensus, prepReq); - Console.WriteLine("Waiting for something related to the PrepRequest...\nNothing happens...Recovery will come due to failed nodes"); - var backupOnRecoveryDueToFailedNodesII = subscriber.ExpectMsg(); - var recoveryPayloadII = (ExtensiblePayload)backupOnRecoveryDueToFailedNodesII.Inventory; - rrm = recoveryPayloadII.Data.AsSerializable(); - Console.WriteLine($"\nAsserting PreparationPayloads is 0..."); - mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(0); - Console.WriteLine($"\nAsserting CountFailed is 6..."); - mockContext.Object.CountFailed.Should().Be(6); - - Console.WriteLine("\nFailed because it is not primary and it created the prereq...Time to adjust"); - ppToSend.ValidatorIndex = 1; //simulating primary as prepreq creator (signature is skip, no problem) - // cleaning old try with Self ValidatorIndex - mockContext.Object.PreparationPayloads[mockContext.Object.MyIndex] = null; - - prepReq = new ExtensiblePayload - { - Category = "Consensus", - ValidBlockStart = 0, - ValidBlockEnd = ppToSend.BlockIndex, - Sender = mockContext.Object.GetSender(ppToSend.ValidatorIndex), - Data = ppToSend.ToArray(), - Witness = prepReq.Witness - }; - TellConsensusPayload(actorConsensus, prepReq); - var OnPrepResponse = subscriber.ExpectMsg(); - var prepResponsePayload = (ExtensiblePayload)OnPrepResponse.Inventory; - PrepareResponse prm = prepResponsePayload.Data.AsSerializable(); - prm.PreparationHash.Should().Be(prepReq.Hash); - Console.WriteLine("\nAsserting PreparationPayloads count is 2..."); - mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(2); - Console.WriteLine($"\nAsserting CountFailed is 5..."); - mockContext.Object.CountFailed.Should().Be(5); - - // Simulating CN 3 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 2)); - //Waiting for RecoveryRequest for a more deterministic UT - backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); - recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory; - rrm = recoveryPayload.Data.AsSerializable(); - rrm.Timestamp.Should().Be(defaultTimestamp); - //Asserts - Console.WriteLine("\nAsserting PreparationPayloads count is 3..."); - mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(3); - Console.WriteLine($"\nAsserting CountFailed is 4..."); - mockContext.Object.CountFailed.Should().Be(4); - - // Simulating CN 5 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 4)); - //Waiting for RecoveryRequest for a more deterministic UT - backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); - recoveryPayload = (ExtensiblePayload)backupOnRecoveryDueToFailedNodes.Inventory; - rrm = recoveryPayload.Data.AsSerializable(); - rrm.Timestamp.Should().Be(defaultTimestamp); - //Asserts - Console.WriteLine("\nAsserting PreparationPayloads count is 4..."); - mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(4); - Console.WriteLine($"\nAsserting CountFailed is 3..."); - mockContext.Object.CountFailed.Should().Be(3); - - // Simulating CN 4 - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, prepResponsePayload, 3)); - var onCommitPayload = subscriber.ExpectMsg(); - var commitPayload = (ExtensiblePayload)onCommitPayload.Inventory; - Commit cm = commitPayload.Data.AsSerializable(); - Console.WriteLine("\nAsserting PreparationPayloads count is 5..."); - mockContext.Object.PreparationPayloads.Count(p => p != null).Should().Be(5); - Console.WriteLine("\nAsserting CountCommitted is 1..."); - mockContext.Object.CountCommitted.Should().Be(1); - Console.WriteLine($"\nAsserting CountFailed is 2..."); - mockContext.Object.CountFailed.Should().Be(2); - - Console.WriteLine($"ORIGINAL BlockHash: {mockContext.Object.Block.Hash}"); - Console.WriteLine($"ORIGINAL Block NextConsensus: {mockContext.Object.Block.NextConsensus}"); - - for (int i = 0; i < mockContext.Object.Validators.Length; i++) - Console.WriteLine($"{mockContext.Object.Validators[i]}/{Contract.CreateSignatureContract(mockContext.Object.Validators[i]).ScriptHash}"); - mockContext.Object.Validators = new ECPoint[7] - { - kp_array[0].PublicKey, - kp_array[1].PublicKey, - kp_array[2].PublicKey, - kp_array[3].PublicKey, - kp_array[4].PublicKey, - kp_array[5].PublicKey, - kp_array[6].PublicKey - }; - Console.WriteLine($"Generated keypairs PKey:"); - //refresh LastSeenMessage - mockContext.Object.LastSeenMessage.Clear(); - for (int i = 0; i < mockContext.Object.Validators.Length; i++) - Console.WriteLine($"{mockContext.Object.Validators[i]}/{Contract.CreateSignatureContract(mockContext.Object.Validators[i]).ScriptHash}"); - var updatedContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators); - Console.WriteLine($"\nContract updated: {updatedContract.ScriptHash}"); - - // =============================================================== - CachedCommittee cachedCommittee = new CachedCommittee(mockContext.Object.Validators.Select(p => (p, BigInteger.Zero))); - mockContext.Object.Snapshot.Storages.Delete(CreateStorageKeyForNativeNeo(14)); - mockContext.Object.Snapshot.Storages.Add(CreateStorageKeyForNativeNeo(14), new StorageItem() - { - Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), 4096) - }); - mockContext.Object.Snapshot.Commit(); - // =============================================================== - - // Forcing next consensus - var originalBlockHashData = mockContext.Object.Block.GetHashData(); - mockContext.Object.Block.NextConsensus = updatedContract.ScriptHash; - mockContext.Object.Block.Header.NextConsensus = updatedContract.ScriptHash; - var originalBlockMerkleRoot = mockContext.Object.Block.MerkleRoot; - Console.WriteLine($"\noriginalBlockMerkleRoot: {originalBlockMerkleRoot}"); - var updatedBlockHashData = mockContext.Object.Block.GetHashData(); - Console.WriteLine($"originalBlockHashData: {originalBlockHashData.ToScriptHash()}"); - Console.WriteLine($"updatedBlockHashData: {updatedBlockHashData.ToScriptHash()}"); - - Console.WriteLine("\n\n=========================="); - Console.WriteLine("\nBasic commits Signatures verification"); - // Basic tests for understanding signatures and ensuring signatures of commits are correct on tests - - - var cmPayloadTemp = GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 6, kp_array[6], updatedBlockHashData); - Crypto.VerifySignature(originalBlockHashData, cm.Signature, mockContext.Object.Validators[0]).Should().BeFalse(); - Crypto.VerifySignature(updatedBlockHashData, cm.Signature, mockContext.Object.Validators[0]).Should().BeFalse(); - Crypto.VerifySignature(originalBlockHashData, cmPayloadTemp.Data.AsSerializable().Signature, mockContext.Object.Validators[6]).Should().BeFalse(); - Crypto.VerifySignature(updatedBlockHashData, cmPayloadTemp.Data.AsSerializable().Signature, mockContext.Object.Validators[6]).Should().BeTrue(); - Console.WriteLine("\n=========================="); - - Console.WriteLine("\n=========================="); - Console.WriteLine("\nCN7 simulation time"); - TellConsensusPayload(actorConsensus, cmPayloadTemp); - var tempPayloadToBlockAndWait = subscriber.ExpectMsg(); - var rmPayload = (ExtensiblePayload)tempPayloadToBlockAndWait.Inventory; - RecoveryMessage rmm = rmPayload.Data.AsSerializable(); - Console.WriteLine("\nAsserting CountCommitted is 2..."); - mockContext.Object.CountCommitted.Should().Be(2); - Console.WriteLine($"\nAsserting CountFailed is 1..."); - mockContext.Object.CountFailed.Should().Be(6); - - Console.WriteLine("\nCN6 simulation time"); - TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 5, kp_array[5], updatedBlockHashData)); - tempPayloadToBlockAndWait = subscriber.ExpectMsg(); - rmPayload = (ExtensiblePayload)tempPayloadToBlockAndWait.Inventory; - rmm = rmPayload.Data.AsSerializable(); - Console.WriteLine("\nAsserting CountCommitted is 3..."); - mockContext.Object.CountCommitted.Should().Be(3); - Console.WriteLine($"\nAsserting CountFailed is 0..."); - mockContext.Object.CountFailed.Should().Be(5); - - Console.WriteLine("\nCN5 simulation time"); - TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 4, kp_array[4], updatedBlockHashData)); - tempPayloadToBlockAndWait = subscriber.ExpectMsg(); - Console.WriteLine("\nAsserting CountCommitted is 4..."); - mockContext.Object.CountCommitted.Should().Be(4); - - // ============================================= - // Testing commit with wrong signature not valid - // It will be invalid signature because we did not change ECPoint - Console.WriteLine("\nCN4 simulation time. Wrong signature, KeyPair is not known"); - TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(mockContext.Object, commitPayload, 3)); - Console.WriteLine("\nWaiting for recovery due to failed nodes... "); - var backupOnRecoveryMessageAfterCommit = subscriber.ExpectMsg(); - rmPayload = (ExtensiblePayload)backupOnRecoveryMessageAfterCommit.Inventory; - rmm = rmPayload.Data.AsSerializable(); - Console.WriteLine("\nAsserting CountCommitted is 4 (Again)..."); - mockContext.Object.CountCommitted.Should().Be(4); - Console.WriteLine("\nAsserting recovery message Preparation is 5..."); - rmm.PreparationMessages.Count().Should().Be(5); - Console.WriteLine("\nAsserting recovery message CommitMessages is 4..."); - rmm.CommitMessages.Count().Should().Be(4); - // ============================================= - - Console.WriteLine($"\nForcing block {mockContext.Object.Block.GetHashData().ToScriptHash()} PrevHash to UInt256.Zero"); - // Another option would be to manipulate Blockchain.Singleton.GetSnapshot().Blocks.GetAndChange - // We would need to get the PrevHash and change the NextConsensus field - var oldPrevHash = mockContext.Object.Block.PrevHash; - mockContext.Object.Block.PrevHash = UInt256.Zero; - //Payload should also be forced, otherwise OnConsensus will not pass - Console.WriteLine($"\nNew Hash is {mockContext.Object.Block.GetHashData().ToScriptHash()}"); - Console.WriteLine($"\nForcing block VerificationScript to {updatedContract.Script.ToScriptHash()}"); - // The default behavior for BlockBase, when PrevHash = UInt256.Zero, is to use its own Witness - mockContext.Object.Block.Witness = new Witness { }; - mockContext.Object.Block.Witness.VerificationScript = updatedContract.Script; - Console.WriteLine($"\nUpdating BlockBase Witness scripthash to: {mockContext.Object.Block.Witness.ScriptHash}"); - Console.WriteLine($"\nNew Hash is {mockContext.Object.Block.GetHashData().ToScriptHash()}"); - - Console.WriteLine("\nCN4 simulation time - Final needed signatures"); - TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(mockContext.Object, commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData())); - - Console.WriteLine("\nWait for subscriber Block"); - var utBlock = subscriber.ExpectMsg(); - Console.WriteLine("\nAsserting CountCommitted is 5..."); - mockContext.Object.CountCommitted.Should().Be(5); - - Console.WriteLine($"\nAsserting block NextConsensus..{utBlock.NextConsensus}"); - utBlock.NextConsensus.Should().Be(updatedContract.ScriptHash); - Console.WriteLine("\n=========================="); - - // ============================================= - Console.WriteLine("\nRecovery simulation..."); - mockContext.Object.CommitPayloads = new ExtensiblePayload[mockContext.Object.Validators.Length]; - // avoiding the BlockSent flag - mockContext.Object.Block.Transactions = null; - // ensuring same hash as snapshot - mockContext.Object.Block.PrevHash = oldPrevHash; - - Console.WriteLine("\nAsserting CountCommitted is 0..."); - mockContext.Object.CountCommitted.Should().Be(0); - Console.WriteLine($"\nAsserting CountFailed is 0..."); - mockContext.Object.CountFailed.Should().Be(3); - Console.WriteLine($"\nModifying CountFailed and asserting 7..."); - // This will ensure a non-deterministic behavior after last recovery - mockContext.Object.LastSeenMessage.Clear(); - mockContext.Object.CountFailed.Should().Be(7); - - TellConsensusPayload(actorConsensus, rmPayload); - - Console.WriteLine("\nWaiting for RecoveryRequest before final asserts..."); - var onRecoveryRequestAfterRecovery = subscriber.ExpectMsg(); - var rrPayload = (ExtensiblePayload)onRecoveryRequestAfterRecovery.Inventory; - var rrMessage = rrPayload.Data.AsSerializable(); - - // It should be 3 because the commit generated by the default wallet is still invalid - Console.WriteLine("\nAsserting CountCommitted is 3 (after recovery)..."); - mockContext.Object.CountCommitted.Should().Be(3); - // ============================================= - - // ============================================= - // ============================================================================ - // finalize ConsensusService actor - // ============================================================================ - Console.WriteLine("Returning states."); - // Updating context.Snapshot with the one that was committed - Console.WriteLine("mockContext Reset for returning Blockchain.Singleton snapshot to original state."); - mockContext.Object.Reset(0); - mockContext.Object.Snapshot.Storages.Delete(CreateStorageKeyForNativeNeo(14)); - cachedCommittee = new CachedCommittee(Blockchain.StandbyCommittee.Select(p => (p, BigInteger.Zero))); - mockContext.Object.Snapshot.Storages.Add(CreateStorageKeyForNativeNeo(14), new StorageItem - { - Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), 4096) - }); - mockContext.Object.Snapshot.Commit(); - - Console.WriteLine("mockContext Reset."); - mockContext.Object.Reset(0); - Console.WriteLine("TimeProvider Reset."); - TimeProvider.ResetToDefault(); - - Console.WriteLine("Finalizing consensus service actor."); - Sys.Stop(actorConsensus); - Console.WriteLine("Actor actorConsensus Stopped.\n"); - } - - /// - /// Get a clone of a ConsensusPayload that contains a Commit Message, change its currentValidatorIndex and sign it - /// - /// ConsensusPayload that will be modified - /// new ValidatorIndex for the cpToCopy - /// KeyPair that will be used for signing the Commit message used for creating blocks - /// HashCode of the Block that is being produced and current being signed - public ExtensiblePayload GetCommitPayloadModifiedAndSignedCopy(ConsensusContext context, ExtensiblePayload cpToCopy, byte vI, KeyPair kp, byte[] blockHashToSign) - { - var cpCommitTemp = cpToCopy.ToArray().AsSerializable(); - cpCommitTemp.Sender = context.GetSender(vI); - var oldMessage = cpCommitTemp.Data.AsSerializable(); - cpCommitTemp.Data = new Commit - { - BlockIndex = oldMessage.BlockIndex, - ValidatorIndex = vI, - ViewNumber = oldMessage.ViewNumber, - Signature = Crypto.Sign(blockHashToSign, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()) - }.ToArray(); - SignPayload(cpCommitTemp, kp); - return cpCommitTemp; - } - - /// - /// Get a clone of a ConsensusPayload and change its currentValidatorIndex - /// - /// ConsensusPayload that will be modified - /// new ValidatorIndex for the cpToCopy - public ExtensiblePayload GetPayloadAndModifyValidator(ConsensusContext context, ExtensiblePayload cpToCopy, byte vI) - { - var cpTemp = cpToCopy.ToArray().AsSerializable(); - var message = ConsensusMessage.DeserializeFrom(cpTemp.Data); - message.ValidatorIndex = vI; - cpTemp.Data = message.ToArray(); - cpTemp.Sender = context.GetSender(vI); - return cpTemp; - } - - private void SignPayload(ExtensiblePayload payload, KeyPair kp) - { - ContractParametersContext sc; - try - { - sc = new ContractParametersContext(payload); - byte[] signature = sc.Verifiable.Sign(kp); - sc.AddSignature(Contract.CreateSignatureContract(kp.PublicKey), kp.PublicKey, signature); - } - catch (InvalidOperationException) - { - return; - } - payload.Witness = sc.GetWitnesses()[0]; - } - - [TestMethod] - public void TestSerializeAndDeserializeConsensusContext() - { - var consensusContext = new ConsensusContext(null, null) - { - Block = new Block - { - PrevHash = Blockchain.GenesisBlock.Hash, - Index = 1, - Timestamp = 4244941711, - NextConsensus = UInt160.Parse("5555AAAA5555AAAA5555AAAA5555AAAA5555AAAA"), - ConsensusData = new ConsensusData - { - PrimaryIndex = 6 - } - }, - ViewNumber = 2, - Validators = new ECPoint[7] - { - ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", Neo.Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", Neo.Cryptography.ECC.ECCurve.Secp256r1) - }, - MyIndex = -1 - }; - var testTx1 = TestUtils.CreateRandomHashTransaction(); - var testTx2 = TestUtils.CreateRandomHashTransaction(); - - int txCountToInlcude = 256; - consensusContext.TransactionHashes = new UInt256[txCountToInlcude]; - - Transaction[] txs = new Transaction[txCountToInlcude]; - for (int i = 0; i < txCountToInlcude; i++) - { - txs[i] = TestUtils.CreateRandomHashTransaction(); - consensusContext.TransactionHashes[i] = txs[i].Hash; - } - // consensusContext.TransactionHashes = new UInt256[2] {testTx1.Hash, testTx2.Hash}; - consensusContext.Transactions = txs.ToDictionary(p => p.Hash); - - consensusContext.PreparationPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; - var prepareRequestMessage = new PrepareRequest - { - PrevHash = consensusContext.Block.PrevHash, - TransactionHashes = consensusContext.TransactionHashes, - Timestamp = 23 - }; - consensusContext.PreparationPayloads[6] = MakeSignedPayload(consensusContext, prepareRequestMessage, 6, new[] { (byte)'3', (byte)'!' }); - consensusContext.PreparationPayloads[0] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash }, 0, new[] { (byte)'t', (byte)'e' }); - consensusContext.PreparationPayloads[1] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash }, 1, new[] { (byte)'s', (byte)'t' }); - consensusContext.PreparationPayloads[2] = null; - consensusContext.PreparationPayloads[3] = MakeSignedPayload(consensusContext, new PrepareResponse { PreparationHash = consensusContext.PreparationPayloads[6].Hash }, 3, new[] { (byte)'1', (byte)'2' }); - consensusContext.PreparationPayloads[4] = null; - consensusContext.PreparationPayloads[5] = null; - - consensusContext.CommitPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; - using (SHA256 sha256 = SHA256.Create()) - { - consensusContext.CommitPayloads[3] = MakeSignedPayload(consensusContext, new Commit { Signature = sha256.ComputeHash(testTx1.Hash.ToArray()).Concat(sha256.ComputeHash(testTx1.Hash.ToArray())).ToArray() }, 3, new[] { (byte)'3', (byte)'4' }); - consensusContext.CommitPayloads[6] = MakeSignedPayload(consensusContext, new Commit { Signature = sha256.ComputeHash(testTx2.Hash.ToArray()).Concat(sha256.ComputeHash(testTx2.Hash.ToArray())).ToArray() }, 3, new[] { (byte)'6', (byte)'7' }); - } - - consensusContext.Block.Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS(); - - consensusContext.ChangeViewPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; - consensusContext.ChangeViewPayloads[0] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 6 }, 0, new[] { (byte)'A' }); - consensusContext.ChangeViewPayloads[1] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 5 }, 1, new[] { (byte)'B' }); - consensusContext.ChangeViewPayloads[2] = null; - consensusContext.ChangeViewPayloads[3] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = uint.MaxValue }, 3, new[] { (byte)'C' }); - consensusContext.ChangeViewPayloads[4] = null; - consensusContext.ChangeViewPayloads[5] = null; - consensusContext.ChangeViewPayloads[6] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 1 }, 6, new[] { (byte)'D' }); - - consensusContext.LastChangeViewPayloads = new ExtensiblePayload[consensusContext.Validators.Length]; - - var copiedContext = TestUtils.CopyMsgBySerialization(consensusContext, new ConsensusContext(null, null)); - - copiedContext.Block.PrevHash.Should().Be(consensusContext.Block.PrevHash); - copiedContext.Block.Index.Should().Be(consensusContext.Block.Index); - copiedContext.ViewNumber.Should().Be(consensusContext.ViewNumber); - copiedContext.Validators.Should().BeEquivalentTo(consensusContext.Validators); - copiedContext.MyIndex.Should().Be(consensusContext.MyIndex); - copiedContext.Block.ConsensusData.PrimaryIndex.Should().Be(consensusContext.Block.ConsensusData.PrimaryIndex); - copiedContext.Block.Timestamp.Should().Be(consensusContext.Block.Timestamp); - copiedContext.Block.NextConsensus.Should().Be(consensusContext.Block.NextConsensus); - copiedContext.TransactionHashes.Should().BeEquivalentTo(consensusContext.TransactionHashes); - copiedContext.Transactions.Should().BeEquivalentTo(consensusContext.Transactions); - copiedContext.Transactions.Values.Should().BeEquivalentTo(consensusContext.Transactions.Values); - copiedContext.PreparationPayloads.Should().BeEquivalentTo(consensusContext.PreparationPayloads); - copiedContext.CommitPayloads.Should().BeEquivalentTo(consensusContext.CommitPayloads); - copiedContext.ChangeViewPayloads.Should().BeEquivalentTo(consensusContext.ChangeViewPayloads); - } - - [TestMethod] - public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepareRequest() - { - var msg = new RecoveryMessage - { - ChangeViewMessages = new Dictionary() - { - { - 0, - new RecoveryMessage.ChangeViewPayloadCompact - { - ValidatorIndex = 0, - OriginalViewNumber = 9, - Timestamp = 6, - InvocationScript = new[] { (byte)'A' } - } - }, - { - 1, - new RecoveryMessage.ChangeViewPayloadCompact - { - ValidatorIndex = 1, - OriginalViewNumber = 7, - Timestamp = 5, - InvocationScript = new[] { (byte)'B' } - } - }, - { - 3, - new RecoveryMessage.ChangeViewPayloadCompact - { - ValidatorIndex = 3, - OriginalViewNumber = 5, - Timestamp = 3, - InvocationScript = new[] { (byte)'C' } - } - }, - { - 6, - new RecoveryMessage.ChangeViewPayloadCompact - { - ValidatorIndex = 6, - OriginalViewNumber = 2, - Timestamp = 1, - InvocationScript = new[] { (byte)'D' } - } - } - }, - PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })), - PreparationMessages = new Dictionary() - { - { - 0, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' } - } - }, - { - 3, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' } - } - }, - { - 6, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 6, - InvocationScript = new[] { (byte)'3', (byte)'!' } - } - } - }, - CommitMessages = new Dictionary() - }; - - // msg.TransactionHashes = null; - // msg.Nonce = 0; - // msg.NextConsensus = null; - // msg.MinerTransaction = (MinerTransaction) null; - msg.PrepareRequestMessage.Should().Be(null); - - var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ; - - copiedMsg.ChangeViewMessages.Should().BeEquivalentTo(msg.ChangeViewMessages); - copiedMsg.PreparationHash.Should().Be(msg.PreparationHash); - copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages); - copiedMsg.CommitMessages.Count.Should().Be(0); - } - - [TestMethod] - public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareRequest() - { - Transaction[] txs = new Transaction[5]; - for (int i = 0; i < txs.Length; i++) - txs[i] = TestUtils.CreateRandomHashTransaction(); - var msg = new RecoveryMessage - { - ChangeViewMessages = new Dictionary() - { - { - 0, - new RecoveryMessage.ChangeViewPayloadCompact - { - ValidatorIndex = 0, - OriginalViewNumber = 9, - Timestamp = 6, - InvocationScript = new[] { (byte)'A' } - } - }, - { - 1, - new RecoveryMessage.ChangeViewPayloadCompact - { - ValidatorIndex = 1, - OriginalViewNumber = 7, - Timestamp = 5, - InvocationScript = new[] { (byte)'B' } - } - }, - { - 3, - new RecoveryMessage.ChangeViewPayloadCompact - { - ValidatorIndex = 3, - OriginalViewNumber = 5, - Timestamp = 3, - InvocationScript = new[] { (byte)'C' } - } - }, - { - 6, - new RecoveryMessage.ChangeViewPayloadCompact - { - ValidatorIndex = 6, - OriginalViewNumber = 2, - Timestamp = 1, - InvocationScript = new[] { (byte)'D' } - } - } - }, - PrepareRequestMessage = new PrepareRequest - { - PrevHash = UInt256.Zero, - TransactionHashes = txs.Select(p => p.Hash).ToArray() - }, - PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })), - PreparationMessages = new Dictionary() - { - { - 0, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' } - } - }, - { - 1, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 1, - InvocationScript = new[] { (byte)'s', (byte)'t' } - } - }, - { - 3, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' } - } - } - }, - CommitMessages = new Dictionary() - }; - - var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ; - - copiedMsg.ChangeViewMessages.Should().BeEquivalentTo(msg.ChangeViewMessages); - copiedMsg.PrepareRequestMessage.Should().BeEquivalentTo(msg.PrepareRequestMessage); - copiedMsg.PreparationHash.Should().Be(null); - copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages); - copiedMsg.CommitMessages.Count.Should().Be(0); - } - - [TestMethod] - public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutCommits() - { - Transaction[] txs = new Transaction[5]; - for (int i = 0; i < txs.Length; i++) - txs[i] = TestUtils.CreateRandomHashTransaction(); - var msg = new RecoveryMessage - { - ChangeViewMessages = new Dictionary(), - PrepareRequestMessage = new PrepareRequest - { - PrevHash = UInt256.Zero, - TransactionHashes = txs.Select(p => p.Hash).ToArray() - }, - PreparationMessages = new Dictionary() - { - { - 0, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' } - } - }, - { - 1, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 1, - InvocationScript = new[] { (byte)'s', (byte)'t' } - } - }, - { - 3, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' } - } - }, - { - 6, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 6, - InvocationScript = new[] { (byte)'3', (byte)'!' } - } - } - }, - CommitMessages = new Dictionary() - }; - - var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ; - - copiedMsg.ChangeViewMessages.Count.Should().Be(0); - copiedMsg.PrepareRequestMessage.Should().BeEquivalentTo(msg.PrepareRequestMessage); - copiedMsg.PreparationHash.Should().Be(null); - copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages); - copiedMsg.CommitMessages.Count.Should().Be(0); - } - - [TestMethod] - public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithCommits() - { - Transaction[] txs = new Transaction[5]; - for (int i = 0; i < txs.Length; i++) - txs[i] = TestUtils.CreateRandomHashTransaction(); - var msg = new RecoveryMessage - { - ChangeViewMessages = new Dictionary(), - PrepareRequestMessage = new PrepareRequest - { - PrevHash = UInt256.Zero, - TransactionHashes = txs.Select(p => p.Hash).ToArray() - }, - PreparationMessages = new Dictionary() - { - { - 0, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 0, - InvocationScript = new[] { (byte)'t', (byte)'e' } - } - }, - { - 1, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 1, - InvocationScript = new[] { (byte)'s', (byte)'t' } - } - }, - { - 3, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 3, - InvocationScript = new[] { (byte)'1', (byte)'2' } - } - }, - { - 6, - new RecoveryMessage.PreparationPayloadCompact - { - ValidatorIndex = 6, - InvocationScript = new[] { (byte)'3', (byte)'!' } - } - } - }, - CommitMessages = new Dictionary - { - { - 1, - new RecoveryMessage.CommitPayloadCompact - { - ValidatorIndex = 1, - Signature = new byte[64] { (byte)'1', (byte)'2', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - InvocationScript = new[] { (byte)'1', (byte)'2' } - } - }, - { - 6, - new RecoveryMessage.CommitPayloadCompact - { - ValidatorIndex = 6, - Signature = new byte[64] { (byte)'3', (byte)'D', (byte)'R', (byte)'I', (byte)'N', (byte)'K', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - InvocationScript = new[] { (byte)'6', (byte)'7' } - } - } - } - }; - - var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ; - - copiedMsg.ChangeViewMessages.Count.Should().Be(0); - copiedMsg.PrepareRequestMessage.Should().BeEquivalentTo(msg.PrepareRequestMessage); - copiedMsg.PreparationHash.Should().Be(null); - copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages); - copiedMsg.CommitMessages.Should().BeEquivalentTo(msg.CommitMessages); - } - - private static ExtensiblePayload MakeSignedPayload(ConsensusContext context, ConsensusMessage message, byte validatorIndex, byte[] witnessInvocationScript) - { - message.BlockIndex = context.Block.Index; - message.ValidatorIndex = validatorIndex; - return new ExtensiblePayload - { - Category = "Consensus", - ValidBlockStart = 0, - ValidBlockEnd = message.BlockIndex, - Sender = context.GetSender(validatorIndex), - Data = message.ToArray(), - Witness = new Witness - { - InvocationScript = witnessInvocationScript, - VerificationScript = Contract.CreateSignatureRedeemScript(context.Validators[validatorIndex]) - } - }; - } - - private StorageKey CreateStorageKeyForNativeNeo(byte prefix) - { - StorageKey storageKey = new StorageKey - { - Id = NativeContract.NEO.Id, - Key = new byte[sizeof(byte)] - }; - storageKey.Key[0] = prefix; - return storageKey; - } - - private void TellConsensusPayload(IActorRef actor, ExtensiblePayload payload) - { - actor.Tell(new Blockchain.RelayResult - { - Inventory = payload, - Result = VerifyResult.Succeed - }); - } - } -} diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs deleted file mode 100644 index 297ad0e17c..0000000000 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs +++ /dev/null @@ -1,193 +0,0 @@ -using Akka.TestKit.Xunit2; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Neo.Consensus; -using Neo.IO; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.SmartContract.Native; -using Neo.Wallets; -using System; -using System.Linq; - -namespace Neo.UnitTests.Consensus -{ - [TestClass] - public class UT_ConsensusContext : TestKit - { - ConsensusContext _context; - KeyPair[] _validatorKeys; - - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - - var rand = new Random(); - var mockWallet = new Mock(); - mockWallet.Setup(p => p.GetAccount(It.IsAny())).Returns(p => new TestWalletAccount(p)); - - // Create dummy validators - - _validatorKeys = new KeyPair[7]; - for (int x = 0; x < _validatorKeys.Length; x++) - { - var pk = new byte[32]; - rand.NextBytes(pk); - - _validatorKeys[x] = new KeyPair(pk); - } - - _context = new ConsensusContext(mockWallet.Object, Blockchain.Singleton.Store) - { - Validators = _validatorKeys.Select(u => u.PublicKey).ToArray() - }; - _context.Reset(0); - } - - [TestCleanup] - public void Cleanup() - { - Shutdown(); - } - - [TestMethod] - public void TestMaxBlockSize_Good() - { - // Only one tx, is included - - var tx1 = CreateTransactionWithSize(200); - _context.EnsureMaxBlockLimitation(new Transaction[] { tx1 }); - EnsureContext(_context, tx1); - - // All txs included - - var max = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(_context.Snapshot); - var txs = new Transaction[max]; - - for (int x = 0; x < max; x++) txs[x] = CreateTransactionWithSize(100); - - _context.EnsureMaxBlockLimitation(txs); - EnsureContext(_context, txs); - } - - [TestMethod] - public void TestMaxBlockSize_Exceed() - { - // Two tx, the last one exceed the size rule, only the first will be included - - var tx1 = CreateTransactionWithSize(200); - var tx2 = CreateTransactionWithSize(256 * 1024); - _context.EnsureMaxBlockLimitation(new Transaction[] { tx1, tx2 }); - EnsureContext(_context, tx1); - - // Exceed txs number, just MaxTransactionsPerBlock included - - var max = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(_context.Snapshot); - var txs = new Transaction[max + 1]; - - for (int x = 0; x < max; x++) txs[x] = CreateTransactionWithSize(100); - - _context.EnsureMaxBlockLimitation(txs); - EnsureContext(_context, txs.Take(max).ToArray()); - } - - [TestMethod] - public void TestMaxBlockSytemFee() - { - var tx1 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2); - - // Less than MaxBlockSystemFee - _context.EnsureMaxBlockLimitation(new Transaction[] { tx1 }); - EnsureContext(_context, new Transaction[] { tx1 }); - - // Equal MaxBlockSystemFee - tx1 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 + 1); - var tx2 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 - 1); - - _context.EnsureMaxBlockLimitation(new Transaction[] { tx1, tx2 }); - EnsureContext(_context, new Transaction[] { tx1, tx2 }); - - // Exceed MaxBlockSystemFee - tx1 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 + 3); - tx2 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 - 3); - var tx3 = CreateTransactionWithSytemFee(NativeContract.Policy.GetMaxBlockSystemFee(_context.Snapshot) / 2 - 4); - - _context.EnsureMaxBlockLimitation(new Transaction[] { tx1, tx2, tx3 }); - EnsureContext(_context, new Transaction[] { tx1, tx2 }); - } - - private Transaction CreateTransactionWithSize(int v) - { - var r = new Random(); - var tx = new Transaction() - { - Attributes = System.Array.Empty(), - Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, - NetworkFee = 0, - Nonce = (uint)Environment.TickCount, - Script = new byte[0], - SystemFee = 0, - ValidUntilBlock = (uint)r.Next(), - Version = 0, - Witnesses = new Witness[0], - }; - - // Could be higher (few bytes) if varSize grows - tx.Script = new byte[v - tx.Size]; - return tx; - } - - private Transaction CreateTransactionWithSytemFee(long fee) - { - var tx = new Transaction() - { - Attributes = System.Array.Empty(), - Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, - NetworkFee = 0, - Nonce = (uint)Environment.TickCount, - Script = new byte[0], - SystemFee = fee, - ValidUntilBlock = int.MaxValue, - Version = 0, - Witnesses = new Witness[0], - }; - return tx; - } - - private Block SignBlock(ConsensusContext context) - { - context.Block.MerkleRoot = null; - - // Fake commits - - for (int x = 0; x < _validatorKeys.Length; x++) - { - _context.MyIndex = x; - - var com = _context.MakeCommit(); - _context.CommitPayloads[_context.MyIndex] = com; - } - - return context.CreateBlock(); - } - - private void EnsureContext(ConsensusContext context, params Transaction[] expected) - { - // Check all tx - - Assert.AreEqual(expected.Length, context.Transactions.Count); - Assert.IsTrue(expected.All(tx => context.Transactions.ContainsKey(tx.Hash))); - - Assert.AreEqual(expected.Length, context.TransactionHashes.Length); - Assert.IsTrue(expected.All(tx => context.TransactionHashes.Count(t => t == tx.Hash) == 1)); - - // Ensure length - - var block = SignBlock(context); - - Assert.AreEqual(context.GetExpectedBlockSize(), block.Size); - Assert.IsTrue(block.Size < NativeContract.Policy.GetMaxBlockSize(context.Snapshot)); - } - } -} diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs deleted file mode 100644 index 9de9617e65..0000000000 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Akka.TestKit.Xunit2; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Consensus; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using System; - -namespace Neo.UnitTests.Consensus -{ - [TestClass] - public class UT_ConsensusServiceMailbox : TestKit - { - private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism - - ConsensusServiceMailbox uut; - - [TestCleanup] - public void Cleanup() - { - Shutdown(); - } - - [TestInitialize] - public void TestSetup() - { - Akka.Actor.ActorSystem system = Sys; - var config = TestKit.DefaultConfig; - var akkaSettings = new Akka.Actor.Settings(system, config); - uut = new ConsensusServiceMailbox(akkaSettings, config); - } - - [TestMethod] - public void ConsensusServiceMailbox_Test_IsHighPriority() - { - // high priority - uut.IsHighPriority(new ExtensiblePayload()).Should().Be(true); - uut.IsHighPriority(new ConsensusService.SetViewNumber()).Should().Be(true); - uut.IsHighPriority(new ConsensusService.Timer()).Should().Be(true); - uut.IsHighPriority(new Blockchain.PersistCompleted()).Should().Be(true); - - // any random object should not have priority - object obj = null; - uut.IsHighPriority(obj).Should().Be(false); - } - } -} diff --git a/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs b/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs deleted file mode 100644 index 3c66f59533..0000000000 --- a/tests/neo.UnitTests/Consensus/UT_RecoveryRequest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Consensus; -using Neo.IO; - -namespace Neo.UnitTests.Consensus -{ - [TestClass] - public class UT_RecoveryRequest - { - [TestMethod] - public void Size_Get() - { - var test = new RecoveryRequest() { Timestamp = 1, ViewNumber = 1 }; - test.Size.Should().Be(15); - } - - [TestMethod] - public void DeserializeAndSerialize() - { - var test = new RecoveryRequest() { ViewNumber = 1, Timestamp = 123 }; - var clone = test.ToArray().AsSerializable(); - - Assert.AreEqual(test.Timestamp, clone.Timestamp); - Assert.AreEqual(test.Type, clone.Type); - Assert.AreEqual(test.ViewNumber, clone.ViewNumber); - } - } -} diff --git a/tests/neo.UnitTests/UT_NeoSystem.cs b/tests/neo.UnitTests/UT_NeoSystem.cs index 5e440d8374..646d214f6c 100644 --- a/tests/neo.UnitTests/UT_NeoSystem.cs +++ b/tests/neo.UnitTests/UT_NeoSystem.cs @@ -22,8 +22,5 @@ public void Setup() [TestMethod] public void TestGetTaskManager() => neoSystem.TaskManager.Should().NotBeNull(); - - [TestMethod] - public void TestGetConsensus() => neoSystem.Consensus.Should().BeNull(); } } From 8b1509f3a2fb7fa62a958a997fef18b7a30cfe1a Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 11 Jan 2021 00:02:57 +0800 Subject: [PATCH 28/65] Send the persisting block to IPersistencePlugin (#2213) --- src/neo/Ledger/Blockchain.cs | 4 ++-- src/neo/Plugins/IPersistencePlugin.cs | 5 +++-- tests/neo.UnitTests/Plugins/UT_Plugin.cs | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index c475228b79..23e87ae05d 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -446,14 +446,14 @@ private void Persist(Block block) all_application_executed.Add(application_executed); } foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) - plugin.OnPersist(snapshot, all_application_executed); + plugin.OnPersist(block, snapshot, all_application_executed); snapshot.Commit(); List commitExceptions = null; foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) { try { - plugin.OnCommit(snapshot); + plugin.OnCommit(block, snapshot); } catch (Exception ex) { diff --git a/src/neo/Plugins/IPersistencePlugin.cs b/src/neo/Plugins/IPersistencePlugin.cs index 9c08677efa..64be4b8b0b 100644 --- a/src/neo/Plugins/IPersistencePlugin.cs +++ b/src/neo/Plugins/IPersistencePlugin.cs @@ -1,3 +1,4 @@ +using Neo.Network.P2P.Payloads; using Neo.Persistence; using System; using System.Collections.Generic; @@ -7,8 +8,8 @@ namespace Neo.Plugins { public interface IPersistencePlugin { - void OnPersist(StoreView snapshot, IReadOnlyList applicationExecutedList) { } - void OnCommit(StoreView snapshot) { } + void OnPersist(Block block, StoreView snapshot, IReadOnlyList applicationExecutedList) { } + void OnCommit(Block block, StoreView snapshot) { } bool ShouldThrowExceptionFromCommit(Exception ex) => false; } } diff --git a/tests/neo.UnitTests/Plugins/UT_Plugin.cs b/tests/neo.UnitTests/Plugins/UT_Plugin.cs index 2d520c0740..97085860b5 100644 --- a/tests/neo.UnitTests/Plugins/UT_Plugin.cs +++ b/tests/neo.UnitTests/Plugins/UT_Plugin.cs @@ -33,8 +33,8 @@ public void TestIPersistencePlugin() // With empty default implementation - pp.OnCommit(null); - pp.OnPersist(null, null); + pp.OnCommit(null, null); + pp.OnPersist(null, null, null); } [TestMethod] From a3d97bc635f082383330889d79f2e2071dc5f162 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 12 Jan 2021 10:04:47 +0100 Subject: [PATCH 29/65] Clean p2p plugin (#2216) --- src/neo/Ledger/Blockchain.cs | 6 ++---- src/neo/Plugins/IP2PPlugin.cs | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 23e87ae05d..bf26f6c70d 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -291,11 +291,9 @@ private void OnInventory(IInventory inventory, bool relay = true) Transaction transaction => OnNewTransaction(transaction), _ => OnNewInventory(inventory) }; - if (result == VerifyResult.Succeed) + if (result == VerifyResult.Succeed && relay) { - if (relay) system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = inventory }); - foreach (IP2PPlugin plugin in Plugin.P2PPlugins) - plugin.OnVerifiedInventory(inventory); + system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = inventory }); } SendRelayResult(inventory, result); } diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs index ea792e721c..5479373ad4 100644 --- a/src/neo/Plugins/IP2PPlugin.cs +++ b/src/neo/Plugins/IP2PPlugin.cs @@ -1,11 +1,9 @@ using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; namespace Neo.Plugins { public interface IP2PPlugin { bool OnP2PMessage(Message message) => true; - void OnVerifiedInventory(IInventory inventory) { } } } From c5a0a72b4cbc1c8ee6af1d9901a18de1fa1bd86e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 13 Jan 2021 16:32:02 +0800 Subject: [PATCH 30/65] Fix AddService (#2221) --- src/neo/Plugins/Plugin.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index fab7736ff5..ddcdd54036 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -64,7 +64,7 @@ protected Plugin() public static void AddService(object service) { - services = services.Add(service); + ImmutableInterlocked.Update(ref services, p => p.Add(service)); } private static bool CheckRequiredServices(Type type) From 58b2a0ab97851fb7ea12640cdaca1f3d416a7874 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 13 Jan 2021 16:58:05 +0800 Subject: [PATCH 31/65] Set Admin to null after transfer (#2222) --- src/neo/SmartContract/Native/NameService.cs | 5 +++++ src/neo/SmartContract/Native/NonfungibleToken.cs | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index 5defadeec3..b9ba12dc19 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -53,6 +53,11 @@ internal override void OnPersist(ApplicationEngine engine) } } + protected override void OnTransferred(ApplicationEngine engine, UInt160 from, NameState token) + { + token.Admin = null; + } + protected override byte[] GetKey(byte[] tokenId) { return Crypto.Hash160(tokenId); diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index b22ff83a54..d246ffb067 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -138,13 +138,14 @@ public IIterator TokensOf(StoreView snapshot, UInt160 owner) protected bool Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) { if (to is null) throw new ArgumentNullException(nameof(to)); - TokenState token = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Token).Add(GetKey(tokenId)))?.GetInteroperable(); - if (token is null) throw new ArgumentException(); + StorageKey key_token = CreateStorageKey(Prefix_Token).Add(GetKey(tokenId)); + TokenState token = engine.Snapshot.Storages.TryGet(key_token)?.GetInteroperable(); UInt160 from = token.Owner; if (!from.Equals(engine.CallingScriptHash) && !engine.CheckWitnessInternal(from)) return false; if (!from.Equals(to)) { + token = engine.Snapshot.Storages.GetAndChange(key_token).GetInteroperable(); StorageKey key_from = CreateStorageKey(Prefix_Account).Add(from); NFTAccountState account = engine.Snapshot.Storages.GetAndChange(key_from).GetInteroperable(); account.Remove(tokenId); @@ -154,11 +155,16 @@ protected bool Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) StorageKey key_to = CreateStorageKey(Prefix_Account).Add(to); account = engine.Snapshot.Storages.GetAndChange(key_to, () => new StorageItem(new NFTAccountState())).GetInteroperable(); account.Add(tokenId); + OnTransferred(engine, from, token); } PostTransfer(engine, from, to, tokenId); return true; } + protected virtual void OnTransferred(ApplicationEngine engine, UInt160 from, TokenState token) + { + } + private void PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, byte[] tokenId) { engine.SendNotification(Hash, "Transfer", From ea9344173b40f060d3b6472c2640c262891f32b3 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 13 Jan 2021 17:02:21 +0800 Subject: [PATCH 32/65] Add IWalletProvider (#2223) --- src/neo/Plugins/IWalletProvider.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/neo/Plugins/IWalletProvider.cs diff --git a/src/neo/Plugins/IWalletProvider.cs b/src/neo/Plugins/IWalletProvider.cs new file mode 100644 index 0000000000..361ba3d9eb --- /dev/null +++ b/src/neo/Plugins/IWalletProvider.cs @@ -0,0 +1,12 @@ +using Neo.Wallets; +using System; + +namespace Neo.Plugins +{ + public interface IWalletProvider + { + event EventHandler WalletOpened; + + Wallet GetWallet(); + } +} From 6129c3bd619e68ca142aec4c3eff57bf939c63a5 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 13 Jan 2021 18:22:16 +0800 Subject: [PATCH 33/65] NeoVM 3.0.0-CI00259 (#2224) --- src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs | 1 + src/neo/neo.csproj | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index 76a8f75842..a31edaa6b5 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -189,6 +189,7 @@ partial class ApplicationEngine [OpCode.REVERSEITEMS] = 1 << 13, [OpCode.REMOVE] = 1 << 4, [OpCode.CLEARITEMS] = 1 << 4, + [OpCode.POPITEM] = 1 << 4, [OpCode.ISNULL] = 1 << 1, [OpCode.ISTYPE] = 1 << 1, [OpCode.CONVERT] = 1 << 11, diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index f0ff860c79..a97d17f2ec 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -29,7 +29,7 @@ - + From cb7023932c063ab038254b92dccb2a0e204ddc24 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 14 Jan 2021 15:21:21 +0800 Subject: [PATCH 34/65] Fix FindOptions (#2231) --- src/neo/SmartContract/ApplicationEngine.Storage.cs | 6 +++++- src/neo/SmartContract/FindOptions.cs | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index a27edd2582..24a7f5d2eb 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -62,7 +62,11 @@ protected internal byte[] Get(StorageContext context, byte[] key) protected internal IIterator Find(StorageContext context, byte[] prefix, FindOptions options) { - if (options.HasFlag(FindOptions.KeysOnly) && options.HasFlag(FindOptions.ValuesOnly)) + if ((options & ~FindOptions.All) != 0) + throw new ArgumentOutOfRangeException(nameof(options)); + if (options.HasFlag(FindOptions.KeysOnly) && (options.HasFlag(FindOptions.ValuesOnly) || options.HasFlag(FindOptions.DeserializeValues) || options.HasFlag(FindOptions.PickField0) || options.HasFlag(FindOptions.PickField1))) + throw new ArgumentException(); + if (options.HasFlag(FindOptions.ValuesOnly) && (options.HasFlag(FindOptions.KeysOnly) || options.HasFlag(FindOptions.RemovePrefix))) throw new ArgumentException(); if (options.HasFlag(FindOptions.PickField0) && options.HasFlag(FindOptions.PickField1)) throw new ArgumentException(); diff --git a/src/neo/SmartContract/FindOptions.cs b/src/neo/SmartContract/FindOptions.cs index 5c42929ae3..9b4c20c2ba 100644 --- a/src/neo/SmartContract/FindOptions.cs +++ b/src/neo/SmartContract/FindOptions.cs @@ -12,6 +12,8 @@ public enum FindOptions : byte ValuesOnly = 1 << 2, DeserializeValues = 1 << 3, PickField0 = 1 << 4, - PickField1 = 1 << 5 + PickField1 = 1 << 5, + + All = KeysOnly | RemovePrefix | ValuesOnly | DeserializeValues | PickField0 | PickField1 } } From df79b42bce1901b2ab1372ade24634e693f6252e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 14 Jan 2021 23:05:47 +0800 Subject: [PATCH 35/65] Fix the properties method of native nft contract (#2232) * Fix the properties method of native nft contract * Fix UT * Add using Co-authored-by: Shargon --- src/neo/SmartContract/Native/NFTState.cs | 5 ++- src/neo/SmartContract/Native/NameService.cs | 9 +++-- .../SmartContract/Native/NonfungibleToken.cs | 5 ++- .../SmartContract/Native/UT_GasToken.cs | 6 ++-- .../SmartContract/Native/UT_NameService.cs | 35 ++++++++++--------- .../SmartContract/Native/UT_NeoToken.cs | 33 +++++++++-------- 6 files changed, 46 insertions(+), 47 deletions(-) diff --git a/src/neo/SmartContract/Native/NFTState.cs b/src/neo/SmartContract/Native/NFTState.cs index cd01e8fbe5..8bcb85948c 100644 --- a/src/neo/SmartContract/Native/NFTState.cs +++ b/src/neo/SmartContract/Native/NFTState.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.IO.Json; using Neo.VM; using Neo.VM.Types; using System.Linq; @@ -14,9 +13,9 @@ public abstract class NFTState : IInteroperable public abstract byte[] Id { get; } - public virtual JObject ToJson() + public virtual Map ToMap(ReferenceCounter referenceCounter) { - return new JObject + return new Map(referenceCounter) { ["name"] = Name, ["description"] = Description diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index b9ba12dc19..409e0fe8bb 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -2,7 +2,6 @@ using Neo.Cryptography; using Neo.IO; -using Neo.IO.Json; using Neo.Ledger; using Neo.Persistence; using Neo.VM; @@ -248,11 +247,11 @@ public class NameState : NFTState public override byte[] Id => Utility.StrictUTF8.GetBytes(Name); - public override JObject ToJson() + public override Map ToMap(ReferenceCounter referenceCounter) { - JObject json = base.ToJson(); - json["expiration"] = Expiration; - return json; + Map map = base.ToMap(referenceCounter); + map["expiration"] = Expiration; + return map; } public override void FromStackItem(StackItem stackItem) diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index d246ffb067..c77fbc70fe 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.IO.Json; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract.Iterators; @@ -106,9 +105,9 @@ public UInt160 OwnerOf(StoreView snapshot, byte[] tokenId) } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public JObject Properties(StoreView snapshot, byte[] tokenId) + public Map Properties(ApplicationEngine engine, byte[] tokenId) { - return snapshot.Storages[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().ToJson(); + return engine.Snapshot.Storages[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().ToMap(engine.ReferenceCounter); } [ContractMethod(0_01000000, CallFlags.ReadStates)] diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs index 0b9f8802c6..7bcfe23bdd 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -92,7 +92,7 @@ public void Check_BalanceOfTransferAndBurn() // Burn - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, 0); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, 0); keyCount = snapshot.Storages.GetChangeSet().Count(); Assert.ThrowsException(() => @@ -127,9 +127,9 @@ public void Check_BalanceOfTransferAndBurn() [TestMethod] public void Check_BadScript() { - var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), _persistingBlock, 0); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), _persistingBlock, 0); - var script = new ScriptBuilder(); + using var script = new ScriptBuilder(); script.Emit(OpCode.NOP); engine.LoadScript(script.ToArray()); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs index 85a5daa48c..b8fef0b50d 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs @@ -108,15 +108,18 @@ public void TestRegister() Assert.IsFalse(Check_Register(snapshot, "neo.com\n", UInt160.Zero, persistingBlock)); // good register + + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); + Assert.IsTrue(NativeContract.NameService.IsAvailable(snapshot, "neo.com")); result = Check_Register(snapshot, "neo.com", UInt160.Zero, persistingBlock); Assert.IsTrue(result); - Assert.AreEqual(31536000u, (uint)NativeContract.NameService.Properties(snapshot, Encoding.UTF8.GetBytes("neo.com"))["expiration"].AsNumber()); + Assert.AreEqual(31536000u, (uint)NativeContract.NameService.Properties(engine, Encoding.UTF8.GetBytes("neo.com"))["expiration"].GetInteger()); Assert.IsFalse(NativeContract.NameService.IsAvailable(snapshot, "neo.com")); var resultInt = Check_Renew(snapshot, "neo.com", UInt160.Zero, persistingBlock); Assert.AreEqual(31536000u * 2, (uint)resultInt); - Assert.AreEqual(31536000u * 2, (uint)NativeContract.NameService.Properties(snapshot, Encoding.UTF8.GetBytes("neo.com"))["expiration"].AsNumber()); + Assert.AreEqual(31536000u * 2, (uint)NativeContract.NameService.Properties(engine, Encoding.UTF8.GetBytes("neo.com"))["expiration"].GetInteger()); Assert.IsFalse(NativeContract.NameService.IsAvailable(snapshot, "neo.com")); } @@ -157,8 +160,8 @@ public void TestSetRecord() internal static bool Check_DeleteRecord(StoreView snapshot, string name, RecordType type, UInt160 signedBy, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); - var script = new ScriptBuilder(); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "deleteRecord", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Integer) { Value = (int)type } @@ -175,8 +178,8 @@ internal static bool Check_DeleteRecord(StoreView snapshot, string name, RecordT internal static bool Check_SetRecord(StoreView snapshot, string name, RecordType type, string data, UInt160 signedBy, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); - var script = new ScriptBuilder(); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setRecord", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Integer) { Value = (int)type }, @@ -194,8 +197,8 @@ internal static bool Check_SetRecord(StoreView snapshot, string name, RecordType internal static BigInteger Check_Renew(StoreView snapshot, string name, UInt160 signedBy, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); - var script = new ScriptBuilder(); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "renew", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name } }); @@ -214,8 +217,8 @@ internal static BigInteger Check_Renew(StoreView snapshot, string name, UInt160 internal static bool Check_SetAdmin(StoreView snapshot, string name, UInt160 admin, UInt160 signedBy, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(admin, signedBy), snapshot, persistingBlock); - var script = new ScriptBuilder(); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(admin, signedBy), snapshot, persistingBlock); + using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setAdmin", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Hash160) { Value = admin } @@ -232,8 +235,8 @@ internal static bool Check_SetAdmin(StoreView snapshot, string name, UInt160 adm internal static bool Check_Register(StoreView snapshot, string name, UInt160 owner, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock); - var script = new ScriptBuilder(); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock); + using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "register", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Hash160) { Value = owner } @@ -253,8 +256,8 @@ internal static bool Check_Register(StoreView snapshot, string name, UInt160 own internal static bool Check_SetPrice(StoreView snapshot, UInt160 signedBy, long price, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); - var script = new ScriptBuilder(); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setPrice", new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) { Value = price } }); engine.LoadScript(script.ToArray(), 0, -1, 0); @@ -268,8 +271,8 @@ internal static bool Check_SetPrice(StoreView snapshot, UInt160 signedBy, long p internal static bool Check_AddRoot(StoreView snapshot, UInt160 signedBy, string root, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); - var script = new ScriptBuilder(); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "addRoot", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = root } }); engine.LoadScript(script.ToArray(), 0, -1, 0); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 133df46bd3..cfd1e714a3 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -430,7 +430,7 @@ public void Check_Initialize() [TestMethod] public void Check_BadScript() { - var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), _persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), _persistingBlock); var script = new ScriptBuilder(); script.Emit(OpCode.NOP); @@ -857,9 +857,9 @@ internal static bool Check_OnPersist(StoreView snapshot, Block persistingBlock) internal static bool Check_PostPersist(StoreView snapshot, Block persistingBlock) { - var script = new ScriptBuilder(); + using var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_NativePostPersist); - var engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, persistingBlock); engine.LoadScript(script.ToArray()); return engine.Execute() == VMState.HALT; @@ -867,11 +867,10 @@ internal static bool Check_PostPersist(StoreView snapshot, Block persistingBlock internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView snapshot, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); - + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 0, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - var script = new ScriptBuilder(); + using var script = new ScriptBuilder(); script.EmitPush("getGasPerBlock"); engine.LoadScript(script.ToArray()); @@ -889,7 +888,7 @@ internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView sn internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreView snapshot, BigInteger gasPerBlock, Block persistingBlock) { UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); @@ -911,12 +910,12 @@ internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreV internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[] pubkey, bool signAccount, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 2, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - var script = new ScriptBuilder(); + using var script = new ScriptBuilder(); if (pubkey is null) script.Emit(OpCode.PUSHNULL); @@ -939,12 +938,12 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] internal static (bool State, bool Result) Check_RegisterValidator(StoreView snapshot, byte[] pubkey, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - var script = new ScriptBuilder(); + using var script = new ScriptBuilder(); script.EmitPush(pubkey); script.EmitPush("registerCandidate"); engine.LoadScript(script.ToArray()); @@ -962,11 +961,11 @@ internal static (bool State, bool Result) Check_RegisterValidator(StoreView snap internal static ECPoint[] Check_GetCommittee(StoreView snapshot, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 0, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - var script = new ScriptBuilder(); + using var script = new ScriptBuilder(); script.EmitPush("getCommittee"); engine.LoadScript(script.ToArray()); @@ -980,11 +979,11 @@ internal static ECPoint[] Check_GetCommittee(StoreView snapshot, Block persistin internal static (BigInteger Value, bool State) Check_UnclaimedGas(StoreView snapshot, byte[] address, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 2, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - var script = new ScriptBuilder(); + using var script = new ScriptBuilder(); script.EmitPush(persistingBlock.Index); script.EmitPush(address); script.EmitPush("unclaimedGas"); @@ -1039,12 +1038,12 @@ internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) internal static (bool State, bool Result) Check_UnregisterCandidate(StoreView snapshot, byte[] pubkey, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - var script = new ScriptBuilder(); + using var script = new ScriptBuilder(); script.EmitPush(pubkey); script.EmitPush("unregisterCandidate"); engine.LoadScript(script.ToArray()); From 56bc2b1d5f3ad6873e23cc6fbd256b124c87477b Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 15 Jan 2021 08:46:30 +0100 Subject: [PATCH 36/65] Update vm (#2225) --- src/neo/SmartContract/ApplicationEngine.cs | 9 ++++----- src/neo/SmartContract/Helper.cs | 2 +- src/neo/Wallets/Wallet.cs | 2 +- src/neo/neo.csproj | 2 +- .../Extensions/NativeContractExtensions.cs | 2 +- .../Extensions/Nep17NativeContractExtensions.cs | 10 +++++----- .../SmartContract/Native/UT_NameService.cs | 14 +++++++------- .../SmartContract/Native/UT_NeoToken.cs | 14 +++++++------- 8 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index f481e27726..b6a9a8de55 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -113,7 +113,7 @@ private ExecutionContext CallContractInternal(ContractState contract, ContractMe if (args.Count != method.Parameters.Length) throw new InvalidOperationException($"Method {method} Expects {method.Parameters.Length} Arguments But Receives {args.Count} Arguments"); if (hasReturnValue ^ (method.ReturnType != ContractParameterType.Void)) throw new InvalidOperationException("The return value type does not match."); - ExecutionContext context_new = LoadContract(contract, method.Name, flags & callingFlags, hasReturnValue, (ushort)args.Count); + ExecutionContext context_new = LoadContract(contract, method.Name, flags & callingFlags, hasReturnValue); state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; @@ -163,13 +163,12 @@ protected override void LoadContext(ExecutionContext context) base.LoadContext(context); } - public ExecutionContext LoadContract(ContractState contract, string method, CallFlags callFlags, bool hasReturnValue, ushort pcount) + public ExecutionContext LoadContract(ContractState contract, string method, CallFlags callFlags, bool hasReturnValue) { ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); if (md is null) return null; ExecutionContext context = LoadScript(contract.Script, - pcount: pcount, rvcount: hasReturnValue ? 1 : 0, initialPosition: md.Offset, configureState: p => @@ -189,10 +188,10 @@ public ExecutionContext LoadContract(ContractState contract, string method, Call return context; } - public ExecutionContext LoadScript(Script script, ushort pcount = 0, int rvcount = -1, int initialPosition = 0, Action configureState = null) + public ExecutionContext LoadScript(Script script, int rvcount = -1, int initialPosition = 0, Action configureState = null) { // Create and configure context - ExecutionContext context = CreateContext(script, pcount, rvcount, initialPosition); + ExecutionContext context = CreateContext(script, rvcount, initialPosition); configureState?.Invoke(context.GetState()); // Load context LoadContext(context); diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index b4d22851da..1fc254d996 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -187,7 +187,7 @@ internal static bool VerifyWitness(this IVerifiable verifiable, StoreView snapsh { ContractState cs = NativeContract.ContractManagement.GetContract(snapshot, hash); if (cs is null) return false; - if (engine.LoadContract(cs, "verify", callFlags, true, 0) is null) + if (engine.LoadContract(cs, "verify", callFlags, true) is null) return false; } else diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 77015abf60..f345a32759 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -397,7 +397,7 @@ public long CalculateNetworkFee(StoreView snapshot, Transaction tx) // Check verify cost using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.Clone()); - if (engine.LoadContract(contract, "verify", CallFlags.None, true, 0) is null) + if (engine.LoadContract(contract, "verify", CallFlags.None, true) is null) throw new ArgumentException($"The smart contract {contract.Hash} haven't got verify method"); if (NativeContract.IsNative(hash)) engine.Push("verify"); if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index a97d17f2ec..f079bc9509 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -29,7 +29,7 @@ - + diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index a98b08b3b8..07f16bc60b 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -106,7 +106,7 @@ public static StackItem Call(this NativeContract contract, StoreView snapshot, I script.EmitPush(args[i]); script.EmitPush(method); - engine.LoadContract(contractState, method, CallFlags.All, contract.Manifest.Abi.GetMethod(method).ReturnType != ContractParameterType.Void, (ushort)args.Length); + engine.LoadContract(contractState, method, CallFlags.All, contract.Manifest.Abi.GetMethod(method).ReturnType != ContractParameterType.Void); engine.LoadScript(script.ToArray()); if (engine.Execute() != VMState.HALT) diff --git a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs index 4c64606ad4..6f5d548a04 100644 --- a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs @@ -40,7 +40,7 @@ public static bool Transfer(this NativeContract contract, StoreView snapshot, by var engine = ApplicationEngine.Create(TriggerType.Application, new ManualWitness(signFrom ? new UInt160(from) : null), snapshot, persistingBlock); - engine.LoadScript(contract.Script, pcount: 4, configureState: p => p.ScriptHash = contract.Hash); + engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.Emit(OpCode.PUSHNULL); @@ -65,7 +65,7 @@ public static BigInteger TotalSupply(this NativeContract contract, StoreView sna { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, pcount: 0, configureState: p => p.ScriptHash = contract.Hash); + engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.EmitPush("totalSupply"); @@ -83,7 +83,7 @@ public static BigInteger BalanceOf(this NativeContract contract, StoreView snaps { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, pcount: 1, configureState: p => p.ScriptHash = contract.Hash); + engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.EmitPush(account); @@ -102,7 +102,7 @@ public static BigInteger Decimals(this NativeContract contract, StoreView snapsh { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, pcount: 0, configureState: p => p.ScriptHash = contract.Hash); + engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.EmitPush("decimals"); @@ -120,7 +120,7 @@ public static string Symbol(this NativeContract contract, StoreView snapshot) { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, pcount: 0, configureState: p => p.ScriptHash = contract.Hash); + engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); var script = new ScriptBuilder(); script.EmitPush("symbol"); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs index b8fef0b50d..507bd4e90c 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs @@ -166,7 +166,7 @@ internal static bool Check_DeleteRecord(StoreView snapshot, string name, RecordT new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Integer) { Value = (int)type } }); - engine.LoadScript(script.ToArray(), 0, -1, 0); + engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) { @@ -185,7 +185,7 @@ internal static bool Check_SetRecord(StoreView snapshot, string name, RecordType new ContractParameter(ContractParameterType.Integer) { Value = (int)type }, new ContractParameter(ContractParameterType.String) { Value = data } }); - engine.LoadScript(script.ToArray(), 0, -1, 0); + engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) { @@ -202,7 +202,7 @@ internal static BigInteger Check_Renew(StoreView snapshot, string name, UInt160 script.EmitDynamicCall(NativeContract.NameService.Hash, "renew", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name } }); - engine.LoadScript(script.ToArray(), 0, -1, 0); + engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) { @@ -223,7 +223,7 @@ internal static bool Check_SetAdmin(StoreView snapshot, string name, UInt160 adm new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Hash160) { Value = admin } }); - engine.LoadScript(script.ToArray(), 0, -1, 0); + engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) { @@ -241,7 +241,7 @@ internal static bool Check_Register(StoreView snapshot, string name, UInt160 own new ContractParameter(ContractParameterType.String) { Value = name }, new ContractParameter(ContractParameterType.Hash160) { Value = owner } }); - engine.LoadScript(script.ToArray(), 0, -1, 0); + engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) { @@ -259,7 +259,7 @@ internal static bool Check_SetPrice(StoreView snapshot, UInt160 signedBy, long p using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setPrice", new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) { Value = price } }); - engine.LoadScript(script.ToArray(), 0, -1, 0); + engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) { @@ -274,7 +274,7 @@ internal static bool Check_AddRoot(StoreView snapshot, UInt160 signedBy, string using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "addRoot", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = root } }); - engine.LoadScript(script.ToArray(), 0, -1, 0); + engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) { diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index cfd1e714a3..abac2d6db6 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -868,7 +868,7 @@ internal static bool Check_PostPersist(StoreView snapshot, Block persistingBlock internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView snapshot, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, pcount: 0, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); using var script = new ScriptBuilder(); script.EmitPush("getGasPerBlock"); @@ -890,7 +890,7 @@ internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreV UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); var script = new ScriptBuilder(); script.EmitPush(gasPerBlock); @@ -913,7 +913,7 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, pcount: 2, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); using var script = new ScriptBuilder(); @@ -941,7 +941,7 @@ internal static (bool State, bool Result) Check_RegisterValidator(StoreView snap using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); using var script = new ScriptBuilder(); script.EmitPush(pubkey); @@ -963,7 +963,7 @@ internal static ECPoint[] Check_GetCommittee(StoreView snapshot, Block persistin { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, pcount: 0, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); using var script = new ScriptBuilder(); script.EmitPush("getCommittee"); @@ -981,7 +981,7 @@ internal static (BigInteger Value, bool State) Check_UnclaimedGas(StoreView snap { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, pcount: 2, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); using var script = new ScriptBuilder(); script.EmitPush(persistingBlock.Index); @@ -1041,7 +1041,7 @@ internal static (bool State, bool Result) Check_UnregisterCandidate(StoreView sn using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, pcount: 1, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); + engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); using var script = new ScriptBuilder(); script.EmitPush(pubkey); From c018d0110755e178597ea44c2e73c73e814b3e52 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 15 Jan 2021 16:36:53 +0800 Subject: [PATCH 37/65] Pass additional data to _deploy (#2239) --- src/neo/SmartContract/Native/ContractManagement.cs | 9 +++++---- .../neo.UnitTests/Extensions/NativeContractExtensions.cs | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index a65d9fd100..8d584dea87 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -5,6 +5,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; +using Neo.VM.Types; using System; using System.Collections.Generic; using System.Linq; @@ -120,7 +121,7 @@ public IEnumerable ListContracts(StoreView snapshot) } [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] - private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest) + private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { if (!(engine.ScriptContainer is Transaction tx)) throw new InvalidOperationException(); @@ -156,7 +157,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy"); if (md != null) - engine.CallFromNativeContract(Hash, hash, md.Name, false); + engine.CallFromNativeContract(Hash, hash, md.Name, data, false); engine.SendNotification(Hash, "Deploy", new VM.Types.Array { contract.Hash.ToArray() }); @@ -164,7 +165,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma } [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] - private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest) + private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { if (nefFile is null && manifest is null) throw new ArgumentException(); @@ -194,7 +195,7 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest) { ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy"); if (md != null) - engine.CallFromNativeContract(Hash, contract.Hash, md.Name, true); + engine.CallFromNativeContract(Hash, contract.Hash, md.Name, data, true); } engine.SendNotification(Hash, "Update", new VM.Types.Array { contract.Hash.ToArray() }); } diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index 07f16bc60b..b8fe4529a2 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -13,7 +13,7 @@ public static class NativeContractExtensions public static ContractState DeployContract(this StoreView snapshot, UInt160 sender, byte[] nefFile, byte[] manifest, long gas = 200_00000000) { var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nefFile, manifest); + script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nefFile, manifest, null); var engine = ApplicationEngine.Create(TriggerType.Application, sender != null ? new Transaction() { Signers = new Signer[] { new Signer() { Account = sender } } } : null, snapshot, null, gas); @@ -34,7 +34,7 @@ public static ContractState DeployContract(this StoreView snapshot, UInt160 send public static void UpdateContract(this StoreView snapshot, UInt160 callingScriptHash, byte[] nefFile, byte[] manifest) { var script = new ScriptBuilder(); - script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "update", nefFile, manifest); + script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "update", nefFile, manifest, null); var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(script.ToArray()); From 91c956c9fa54ba5c8ef0ddc7b257d806db0d1928 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 15 Jan 2021 16:39:35 +0800 Subject: [PATCH 38/65] Disallow the modification of contract name (#2236) --- src/neo/SmartContract/Native/ContractManagement.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 8d584dea87..07d9377288 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -186,9 +186,12 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, S { if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) throw new ArgumentException($"Invalid Manifest Length: {manifest.Length}"); - contract.Manifest = ContractManifest.Parse(manifest); - if (!contract.Manifest.IsValid(contract.Hash)) + ContractManifest manifest_new = ContractManifest.Parse(manifest); + if (manifest_new.Name != contract.Manifest.Name) + throw new InvalidOperationException("The name of the contract can't be changed."); + if (!manifest_new.IsValid(contract.Hash)) throw new InvalidOperationException($"Invalid Manifest Hash: {contract.Hash}"); + contract.Manifest = manifest_new; } contract.UpdateCounter++; // Increase update counter if (nefFile != null) From d9f7a3c1b853ac1a7799dc6c4069b91394aa9b3e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 15 Jan 2021 16:48:33 +0800 Subject: [PATCH 39/65] Merge the compiler and version fields of NefFile (#2235) --- .../SmartContract/Native/NativeContract.cs | 3 +-- src/neo/SmartContract/NefFile.cs | 23 +++++-------------- .../SmartContract/UT_ContractState.cs | 1 - .../neo.UnitTests/SmartContract/UT_Helper.cs | 1 - .../SmartContract/UT_InteropService.NEO.cs | 5 ---- .../neo.UnitTests/SmartContract/UT_NefFile.cs | 5 ---- 6 files changed, 7 insertions(+), 31 deletions(-) diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index fd47d5d6e2..eeeb21f8ce 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -50,8 +50,7 @@ protected NativeContract() } this.Nef = new NefFile { - Compiler = nameof(ScriptBuilder), - Version = "3.0", + Compiler = "neo-core-v3.0", Tokens = System.Array.Empty(), Script = script }; diff --git a/src/neo/SmartContract/NefFile.cs b/src/neo/SmartContract/NefFile.cs index 7eac98a35d..e43bf67433 100644 --- a/src/neo/SmartContract/NefFile.cs +++ b/src/neo/SmartContract/NefFile.cs @@ -14,8 +14,7 @@ namespace Neo.SmartContract /// │ Field │ Type │ Comment │ /// ├──────────┼───────────────┼────────────────────────────────────────────┤ /// │ Magic │ uint32 │ Magic header │ - /// │ Compiler │ byte[32] │ Compiler used │ - /// │ Version │ byte[32] │ Compiler version │ + /// │ Compiler │ byte[64] │ Compiler name and version │ /// ├──────────┼───────────────┼────────────────────────────────────────────┤ /// │ Reserve │ byte[2] │ Reserved for future extensions. Must be 0. │ /// │ Tokens │ MethodToken[] │ Method tokens. │ @@ -33,15 +32,10 @@ public class NefFile : ISerializable private const uint Magic = 0x3346454E; /// - /// Compiler + /// Compiler name and version /// public string Compiler { get; set; } - /// - /// Version - /// - public string Version { get; set; } - /// /// Method tokens /// @@ -60,8 +54,8 @@ public class NefFile : ISerializable public const int MaxScriptLength = 512 * 1024; private const int HeaderSize = - sizeof(uint) + // Magic - (32 * 2); // Compiler+Version + sizeof(uint) + // Magic + 64; // Compiler public int Size => HeaderSize + // Header @@ -84,18 +78,14 @@ public void Serialize(BinaryWriter writer) private void SerializeHeader(BinaryWriter writer) { writer.Write(Magic); - writer.WriteFixedString(Compiler, 32); - - // Version - writer.WriteFixedString(Version, 32); + writer.WriteFixedString(Compiler, 64); } public void Deserialize(BinaryReader reader) { if (reader.ReadUInt32() != Magic) throw new FormatException("Wrong magic"); - Compiler = reader.ReadFixedString(32); - Version = reader.ReadFixedString(32); + Compiler = reader.ReadFixedString(64); if (reader.ReadUInt16() != 0) throw new FormatException("Reserved bytes must be 0"); @@ -126,7 +116,6 @@ public JObject ToJson() { ["magic"] = Magic, ["compiler"] = Compiler, - ["version"] = Version, ["tokens"] = new JArray(Tokens.Select(p => p.ToJson())), ["script"] = Convert.ToBase64String(Script), ["checksum"] = CheckSum diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractState.cs b/tests/neo.UnitTests/SmartContract/UT_ContractState.cs index 77431c08fa..72bd25e175 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractState.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractState.cs @@ -24,7 +24,6 @@ public void TestSetup() Nef = new NefFile { Compiler = nameof(ScriptBuilder), - Version = typeof(ScriptBuilder).Assembly.GetVersion(), Tokens = Array.Empty(), Script = script }, diff --git a/tests/neo.UnitTests/SmartContract/UT_Helper.cs b/tests/neo.UnitTests/SmartContract/UT_Helper.cs index 881bc7845f..609110999d 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Helper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Helper.cs @@ -13,7 +13,6 @@ public void TestGetContractHash() var nef = new NefFile() { Compiler = "test", - Version = new System.Version().ToString(), Tokens = Array.Empty(), Script = new byte[] { 1, 2, 3 } }; diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 477eccf54e..131c3779a4 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -135,7 +135,6 @@ public void TestContract_Create() { Script = new byte[byte.MaxValue], Compiler = "", - Version = new Version(1, 2, 3, 4).ToString(), Tokens = System.Array.Empty() }; nef.CheckSum = NefFile.ComputeChecksum(nef); @@ -149,7 +148,6 @@ public void TestContract_Create() { Script = new byte[NefFile.MaxScriptLength - 1], Compiler = "", - Version = new Version(1, 2, 3, 4).ToString(), Tokens = System.Array.Empty() }; script_exceedMaxLength.CheckSum = NefFile.ComputeChecksum(nef); @@ -180,7 +178,6 @@ public void TestContract_Update() { Script = new byte[] { 0x01 }, Compiler = "", - Version = new Version(1, 2, 3, 4).ToString(), Tokens = System.Array.Empty() }; nef.CheckSum = NefFile.ComputeChecksum(nef); @@ -231,7 +228,6 @@ public void TestContract_Update_Invalid() var nefFile = new NefFile() { Script = new byte[] { 0x01 }, - Version = new Version(1, 2, 3, 4).ToString(), Compiler = "", Tokens = System.Array.Empty() }; @@ -246,7 +242,6 @@ public void TestContract_Update_Invalid() nefFile = new NefFile() { Script = new byte[0], - Version = new Version(1, 2, 3, 4).ToString(), Compiler = "", Tokens = System.Array.Empty() }; diff --git a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs b/tests/neo.UnitTests/SmartContract/UT_NefFile.cs index 575b4a0549..2f6d92854b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs +++ b/tests/neo.UnitTests/SmartContract/UT_NefFile.cs @@ -13,7 +13,6 @@ public class UT_NefFile public NefFile file = new NefFile() { Compiler = "".PadLeft(32, ' '), - Version = new Version(1, 2, 3, 4).ToString(), Tokens = Array.Empty(), Script = new byte[] { 0x01, 0x02, 0x03 } }; @@ -70,7 +69,6 @@ public void TestDeserialize() file.CheckSum = NefFile.ComputeChecksum(file); var data = file.ToArray(); var newFile1 = data.AsSerializable(); - newFile1.Version.Should().Be(file.Version); newFile1.Compiler.Should().Be(file.Compiler); newFile1.CheckSum.Should().Be(file.CheckSum); newFile1.Script.Should().BeEquivalentTo(file.Script); @@ -88,7 +86,6 @@ public void ParseTest() var file = new NefFile() { Compiler = "".PadLeft(32, ' '), - Version = new Version(1, 2, 3, 4).ToString(), Tokens = Array.Empty(), Script = new byte[] { 0x01, 0x02, 0x03 } }; @@ -99,7 +96,6 @@ public void ParseTest() file = data.AsSerializable(); Assert.AreEqual("".PadLeft(32, ' '), file.Compiler); - Assert.AreEqual(new Version(1, 2, 3, 4).ToString(), file.Version); CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, file.Script); } @@ -109,7 +105,6 @@ public void LimitTest() var file = new NefFile() { Compiler = "".PadLeft(byte.MaxValue, ' '), - Version = new Version(1, 2, 3, 4).ToString(), Tokens = Array.Empty(), Script = new byte[1024 * 1024], CheckSum = 0 From 39c18908e49ca3dcc1533f685cbf39d5fcd29d94 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 16 Jan 2021 20:11:12 +0800 Subject: [PATCH 40/65] Allow method overloads (#2237) --- .../ApplicationEngine.Contract.cs | 4 ++-- src/neo/SmartContract/ApplicationEngine.cs | 17 +++++++-------- src/neo/SmartContract/DeployedContract.cs | 2 +- src/neo/SmartContract/Helper.cs | 6 ++++-- src/neo/SmartContract/Manifest/ContractAbi.cs | 21 +++++++++++++------ .../Native/ContractManagement.cs | 4 ++-- src/neo/Wallets/Wallet.cs | 8 +++++-- .../Extensions/NativeContractExtensions.cs | 3 ++- .../SmartContract/Native/UT_NativeContract.cs | 4 ++-- 9 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 0d0312849d..e08a17099b 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -30,8 +30,8 @@ protected internal void CallContract(UInt160 contractHash, string method, CallFl ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, contractHash); if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}"); - ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); - if (md is null) throw new InvalidOperationException($"Method {method} Does Not Exist In Contract {contractHash}"); + ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method, args.Count); + if (md is null) throw new InvalidOperationException($"Method \"{method}\" with {args.Count} parameter(s) doesn't exist in the contract {contractHash}."); bool hasReturnValue = md.ReturnType != ContractParameterType.Void; if (!hasReturnValue) CurrentContext.EvaluationStack.Push(StackItem.Null); diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index b6a9a8de55..117ddfb059 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -80,8 +80,8 @@ private ExecutionContext CallContractInternal(UInt160 contractHash, string metho { ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, contractHash); if (contract is null) throw new InvalidOperationException($"Called Contract Does Not Exist: {contractHash}"); - ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); - if (md is null) throw new InvalidOperationException($"Method {method} Does Not Exist In Contract {contractHash}"); + ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method, args.Length); + if (md is null) throw new InvalidOperationException($"Method \"{method}\" with {args.Length} parameter(s) doesn't exist in the contract {contractHash}."); return CallContractInternal(contract, md, flags, hasReturnValue, args); } @@ -113,7 +113,7 @@ private ExecutionContext CallContractInternal(ContractState contract, ContractMe if (args.Count != method.Parameters.Length) throw new InvalidOperationException($"Method {method} Expects {method.Parameters.Length} Arguments But Receives {args.Count} Arguments"); if (hasReturnValue ^ (method.ReturnType != ContractParameterType.Void)) throw new InvalidOperationException("The return value type does not match."); - ExecutionContext context_new = LoadContract(contract, method.Name, flags & callingFlags, hasReturnValue); + ExecutionContext context_new = LoadContract(contract, method, flags & callingFlags); state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; @@ -163,14 +163,11 @@ protected override void LoadContext(ExecutionContext context) base.LoadContext(context); } - public ExecutionContext LoadContract(ContractState contract, string method, CallFlags callFlags, bool hasReturnValue) + public ExecutionContext LoadContract(ContractState contract, ContractMethodDescriptor method, CallFlags callFlags) { - ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); - if (md is null) return null; - ExecutionContext context = LoadScript(contract.Script, - rvcount: hasReturnValue ? 1 : 0, - initialPosition: md.Offset, + rvcount: method.ReturnType == ContractParameterType.Void ? 0 : 1, + initialPosition: method.Offset, configureState: p => { p.CallFlags = callFlags; @@ -179,7 +176,7 @@ public ExecutionContext LoadContract(ContractState contract, string method, Call }); // Call initialization - var init = contract.Manifest.Abi.GetMethod("_initialize"); + var init = contract.Manifest.Abi.GetMethod("_initialize", 0); if (init != null) { LoadContext(context.Clone(init.Offset)); diff --git a/src/neo/SmartContract/DeployedContract.cs b/src/neo/SmartContract/DeployedContract.cs index c784811b5e..4a5e3ba2a5 100644 --- a/src/neo/SmartContract/DeployedContract.cs +++ b/src/neo/SmartContract/DeployedContract.cs @@ -14,7 +14,7 @@ public DeployedContract(ContractState contract) Script = null; ScriptHash = contract.Hash; - ContractMethodDescriptor descriptor = contract.Manifest.Abi.GetMethod("verify"); + ContractMethodDescriptor descriptor = contract.Manifest.Abi.GetMethod("verify", -1); if (descriptor is null) throw new NotSupportedException("The smart contract haven't got verify method."); ParameterList = descriptor.Parameters.Select(u => u.Type).ToArray(); diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 1fc254d996..c6091f0be6 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -2,6 +2,7 @@ using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; using Neo.Persistence; +using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; using Neo.VM; using System; @@ -187,8 +188,9 @@ internal static bool VerifyWitness(this IVerifiable verifiable, StoreView snapsh { ContractState cs = NativeContract.ContractManagement.GetContract(snapshot, hash); if (cs is null) return false; - if (engine.LoadContract(cs, "verify", callFlags, true) is null) - return false; + ContractMethodDescriptor md = cs.Manifest.Abi.GetMethod("verify", -1); + if (md?.ReturnType != ContractParameterType.Boolean) return false; + engine.LoadContract(cs, md, callFlags); } else { diff --git a/src/neo/SmartContract/Manifest/ContractAbi.cs b/src/neo/SmartContract/Manifest/ContractAbi.cs index a6cd7d0af1..5067cf9552 100644 --- a/src/neo/SmartContract/Manifest/ContractAbi.cs +++ b/src/neo/SmartContract/Manifest/ContractAbi.cs @@ -1,15 +1,16 @@ using Neo.IO.Json; +using System; using System.Collections.Generic; using System.Linq; namespace Neo.SmartContract.Manifest { /// - /// For technical details of ABI, please refer to NEP-3: NeoContract ABI. (https://github.com/neo-project/proposals/blob/master/nep-3.mediawiki) + /// NeoContract ABI /// public class ContractAbi { - private IReadOnlyDictionary methodDictionary; + private IReadOnlyDictionary<(string, int), ContractMethodDescriptor> methodDictionary; /// /// Methods is an array of Method objects which describe the details of each method in the contract. @@ -44,11 +45,19 @@ public static ContractAbi FromJson(JObject json) }; } - public ContractMethodDescriptor GetMethod(string name) + public ContractMethodDescriptor GetMethod(string name, int pcount) { - methodDictionary ??= Methods.ToDictionary(p => p.Name); - methodDictionary.TryGetValue(name, out var method); - return method; + if (pcount < -1 || pcount > ushort.MaxValue) throw new ArgumentOutOfRangeException(nameof(pcount)); + if (pcount >= 0) + { + methodDictionary ??= Methods.ToDictionary(p => (p.Name, p.Parameters.Length)); + methodDictionary.TryGetValue((name, pcount), out var method); + return method; + } + else + { + return Methods.FirstOrDefault(p => p.Name == name); + } } public JObject ToJson() diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 07d9377288..1bbfbac331 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -155,7 +155,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma // Execute _deploy - ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy"); + ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy", 2); if (md != null) engine.CallFromNativeContract(Hash, hash, md.Name, data, false); @@ -196,7 +196,7 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, S contract.UpdateCounter++; // Increase update counter if (nefFile != null) { - ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy"); + ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy", 2); if (md != null) engine.CallFromNativeContract(Hash, contract.Hash, md.Name, data, true); } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index f345a32759..141b21eec7 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -391,14 +391,18 @@ public long CalculateNetworkFee(StoreView snapshot, Transaction tx) { var contract = NativeContract.ContractManagement.GetContract(snapshot, hash); if (contract is null) continue; + var md = contract.Manifest.Abi.GetMethod("verify", 0); + if (md is null) + throw new ArgumentException($"The smart contract {contract.Hash} haven't got verify method without arguments"); + if (md.ReturnType != ContractParameterType.Boolean) + throw new ArgumentException("The verify method doesn't return boolean value."); // Empty invocation and verification scripts size += Array.Empty().GetVarSize() * 2; // Check verify cost using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.Clone()); - if (engine.LoadContract(contract, "verify", CallFlags.None, true) is null) - throw new ArgumentException($"The smart contract {contract.Hash} haven't got verify method"); + engine.LoadContract(contract, md, CallFlags.None); if (NativeContract.IsNative(hash)) engine.Push("verify"); if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); if (!engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false."); diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index b8fe4529a2..2e2b64ba64 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -99,6 +99,7 @@ public static StackItem Call(this NativeContract contract, StoreView snapshot, I var engine = ApplicationEngine.Create(TriggerType.Application, container, snapshot, persistingBlock); var contractState = NativeContract.ContractManagement.GetContract(snapshot, contract.Hash); if (contractState == null) throw new InvalidOperationException(); + var md = contract.Manifest.Abi.GetMethod(method, args.Length); var script = new ScriptBuilder(); @@ -106,7 +107,7 @@ public static StackItem Call(this NativeContract contract, StoreView snapshot, I script.EmitPush(args[i]); script.EmitPush(method); - engine.LoadContract(contractState, method, CallFlags.All, contract.Manifest.Abi.GetMethod(method).ReturnType != ContractParameterType.Void); + engine.LoadContract(contractState, md, CallFlags.All); engine.LoadScript(script.ToArray()); if (engine.Execute() != VMState.HALT) diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 601c21ab52..d8704b614a 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -51,7 +51,7 @@ public void VMTypes( public void TestToParameter() { var manifest = new DummyNative().Manifest; - var netTypes = manifest.Abi.GetMethod("netTypes"); + var netTypes = manifest.Abi.GetMethod("netTypes", 17); Assert.AreEqual(netTypes.ReturnType, ContractParameterType.Void); Assert.AreEqual(netTypes.Parameters[0].Type, ContractParameterType.Boolean); @@ -72,7 +72,7 @@ public void TestToParameter() Assert.AreEqual(netTypes.Parameters[15].Type, ContractParameterType.Integer); Assert.AreEqual(netTypes.Parameters[16].Type, ContractParameterType.Any); - var vmTypes = manifest.Abi.GetMethod("vMTypes"); + var vmTypes = manifest.Abi.GetMethod("vMTypes", 8); Assert.AreEqual(vmTypes.ReturnType, ContractParameterType.Void); Assert.AreEqual(vmTypes.Parameters[0].Type, ContractParameterType.Boolean); From df7070d4228bbcc3026f34cbbe389f0eb8c0c9ad Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 17 Jan 2021 21:58:27 +0800 Subject: [PATCH 41/65] Deterministic methods order for native contracts (#2241) Co-authored-by: Shargon --- src/neo/SmartContract/Native/NativeContract.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index eeeb21f8ce..64fc0544a8 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -75,11 +75,11 @@ protected NativeContract() { Name = Name, Groups = System.Array.Empty(), - SupportedStandards = new string[0], + SupportedStandards = System.Array.Empty(), Abi = new ContractAbi() { Events = System.Array.Empty(), - Methods = descriptors.ToArray() + Methods = descriptors.OrderBy(p => p.Name).ThenBy(p => p.Parameters.Length).ToArray() }, Permissions = new[] { ContractPermission.DefaultPermission }, Trusts = WildcardContainer.Create(), From 14529e6520652d77899520026acbf10d78476068 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 19 Jan 2021 17:26:13 +0800 Subject: [PATCH 42/65] Add overloads on ContractManagement (#2242) --- src/neo/SmartContract/Native/ContractManagement.cs | 12 ++++++++++++ src/neo/SmartContract/Native/NativeContract.cs | 6 +++--- .../SmartContract/Native/UT_NativeContract.cs | 5 ----- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 1bbfbac331..229dbecee7 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -120,6 +120,12 @@ public IEnumerable ListContracts(StoreView snapshot) return snapshot.Storages.Find(listContractsPrefix).Select(kvp => kvp.Value.GetInteroperable()); } + [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] + private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest) + { + return Deploy(engine, nefFile, manifest, StackItem.Null); + } + [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { @@ -164,6 +170,12 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma return contract; } + [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] + private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest) + { + Update(engine, nefFile, manifest, StackItem.Null); + } + [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 64fc0544a8..1f2443b79c 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -16,7 +16,7 @@ public abstract class NativeContract private static readonly List contractsList = new List(); private static readonly Dictionary contractsIdDictionary = new Dictionary(); private static readonly Dictionary contractsHashDictionary = new Dictionary(); - private readonly Dictionary methods = new Dictionary(); + private readonly Dictionary<(string, int), ContractMethodMetadata> methods = new Dictionary<(string, int), ContractMethodMetadata>(); private static int id_counter = 0; #region Named Native Contracts @@ -69,7 +69,7 @@ protected NativeContract() Parameters = metadata.Parameters.Select(p => new ContractParameterDefinition { Type = ToParameterType(p.Type), Name = p.Name }).ToArray(), Safe = (attribute.RequiredCallFlags & ~CallFlags.ReadOnly) == 0 }); - methods.Add(metadata.Name, metadata); + methods.Add((metadata.Name, metadata.Parameters.Length), metadata); } this.Manifest = new ContractManifest { @@ -121,7 +121,7 @@ internal void Invoke(ApplicationEngine engine) throw new InvalidOperationException("It is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used."); ExecutionContext context = engine.CurrentContext; string operation = context.EvaluationStack.Pop().GetString(); - ContractMethodMetadata method = methods[operation]; + ContractMethodMetadata method = methods[(operation, context.EvaluationStack.Count)]; ExecutionContextState state = context.GetState(); if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}."); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index d8704b614a..4f378c13e1 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -8,7 +8,6 @@ using System; using System.Collections.Generic; using System.Numerics; -using VMArray = Neo.VM.Types.Array; namespace Neo.UnitTests.SmartContract.Native { @@ -100,14 +99,10 @@ public void TestInvoke() engine.LoadScript(testNativeContract.Script, configureState: p => p.ScriptHash = testNativeContract.Hash); ByteString method1 = new ByteString(System.Text.Encoding.Default.GetBytes("wrongMethod")); - VMArray args1 = new VMArray(); - engine.CurrentContext.EvaluationStack.Push(args1); engine.CurrentContext.EvaluationStack.Push(method1); Assert.ThrowsException(() => testNativeContract.Invoke(engine)); ByteString method2 = new ByteString(System.Text.Encoding.Default.GetBytes("helloWorld")); - VMArray args2 = new VMArray(); - engine.CurrentContext.EvaluationStack.Push(args2); engine.CurrentContext.EvaluationStack.Push(method2); testNativeContract.Invoke(engine); } From 4a4b16b21db5e6e4c381eb72df7ac88a0ceac9e0 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 19 Jan 2021 10:37:00 +0100 Subject: [PATCH 43/65] Add decimal constructor to BigInteger (#2246) --- src/neo/BigDecimal.cs | 6 ++++++ tests/neo.UnitTests/UT_BigDecimal.cs | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/neo/BigDecimal.cs b/src/neo/BigDecimal.cs index aaf541caa9..ad1948f6bc 100644 --- a/src/neo/BigDecimal.cs +++ b/src/neo/BigDecimal.cs @@ -18,6 +18,12 @@ public BigDecimal(BigInteger value, byte decimals) this.decimals = decimals; } + public BigDecimal(decimal value) + { + this.decimals = BitConverter.GetBytes(decimal.GetBits(value)[3])[2]; + this.value = new BigInteger(decimal.Multiply((Decimal)Math.Pow(10, this.decimals), value)); + } + public BigDecimal ChangeDecimals(byte decimals) { if (this.decimals == decimals) return this; diff --git a/tests/neo.UnitTests/UT_BigDecimal.cs b/tests/neo.UnitTests/UT_BigDecimal.cs index cdde9f9e89..ccd147427a 100644 --- a/tests/neo.UnitTests/UT_BigDecimal.cs +++ b/tests/neo.UnitTests/UT_BigDecimal.cs @@ -30,12 +30,34 @@ public void TestBigDecimalConstructor() BigDecimal value = new BigDecimal(new BigInteger(45600), 7); value.Value.Should().Be(new BigInteger(45600)); value.Decimals.Should().Be(7); + value = new BigDecimal(new BigInteger(0), 5); value.Value.Should().Be(new BigInteger(0)); value.Decimals.Should().Be(5); + value = new BigDecimal(new BigInteger(-10), 0); value.Value.Should().Be(new BigInteger(-10)); value.Decimals.Should().Be(0); + + value = new BigDecimal(123.456789M); + value.Value.Should().Be(new BigInteger(123456789)); + value.Decimals.Should().Be(6); + + value = new BigDecimal(-123.45M); + value.Value.Should().Be(new BigInteger(-12345)); + value.Decimals.Should().Be(2); + + value = new BigDecimal(123.45M); + value.Value.Should().Be(new BigInteger(12345)); + value.Decimals.Should().Be(2); + + value = new BigDecimal(123M); + value.Value.Should().Be(new BigInteger(123)); + value.Decimals.Should().Be(0); + + value = new BigDecimal(0M); + value.Value.Should().Be(new BigInteger(0)); + value.Decimals.Should().Be(0); } [TestMethod] From dcde99ed5d48987412541571eaef7ac09d01cb4d Mon Sep 17 00:00:00 2001 From: cn1010 Date: Tue, 19 Jan 2021 17:43:31 +0800 Subject: [PATCH 44/65] update summary info (#2249) Co-authored-by: Erik Zhang --- src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs | 2 +- src/neo/SmartContract/Manifest/ContractParameterDefinition.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs index fbf4188358..3ffbc544c6 100644 --- a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -16,7 +16,7 @@ public int Offset /// /// Returntype indicates the return type of the method. It can be one of the following values: - /// Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, InteropInterface, Void. + /// Any, Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, Map, InteropInterface, Void. /// public ContractParameterType ReturnType { get; set; } diff --git a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs index d5b95f5245..2c1ec6bdac 100644 --- a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -12,7 +12,7 @@ public class ContractParameterDefinition /// /// Type indicates the type of the parameter. It can be one of the following values: - /// Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, InteropInterface. + /// Any, Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, Map, InteropInterface. /// public ContractParameterType Type { get; set; } From 5f17eeee071a0d79dbe03342d8d980a2e237e9f2 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 19 Jan 2021 10:45:52 +0100 Subject: [PATCH 45/65] Change smart contract hash computation (#2240) * CreateChild * Change to checkSum * Remove nonce * Change manifest to name * Remove new method * Clean changes --- src/neo/SmartContract/Helper.cs | 5 +++-- src/neo/SmartContract/Native/ContractManagement.cs | 7 ++++--- src/neo/SmartContract/Native/NativeContract.cs | 2 +- tests/neo.UnitTests/SmartContract/UT_Helper.cs | 4 ++-- tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs | 2 +- tests/neo.UnitTests/VM/UT_Helper.cs | 2 +- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index c6091f0be6..e10c076829 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -28,12 +28,13 @@ public static long MultiSignatureContractCost(int m, int n) => ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.ECDsaVerifyPrice * n; - public static UInt160 GetContractHash(UInt160 sender, byte[] script) + public static UInt160 GetContractHash(UInt160 sender, uint nefCheckSum, string name) { using var sb = new ScriptBuilder(); sb.Emit(OpCode.ABORT); sb.EmitPush(sender); - sb.EmitPush(script); + sb.EmitPush(nefCheckSum); + sb.EmitPush(name); return sb.ToArray().ToScriptHash(); } diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 229dbecee7..4d6697c8b3 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -129,7 +129,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { - if (!(engine.ScriptContainer is Transaction tx)) + if (engine.ScriptContainer is not Transaction tx) throw new InvalidOperationException(); if (nefFile.Length == 0) throw new ArgumentException($"Invalid NefFile Length: {nefFile.Length}"); @@ -142,7 +142,8 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma )); NefFile nef = nefFile.AsSerializable(); - UInt160 hash = Helper.GetContractHash(tx.Sender, nef.Script); + ContractManifest parsedManifest = ContractManifest.Parse(manifest); + UInt160 hash = Helper.GetContractHash(tx.Sender, nef.CheckSum, parsedManifest.Name); StorageKey key = CreateStorageKey(Prefix_Contract).Add(hash); if (engine.Snapshot.Storages.Contains(key)) throw new InvalidOperationException($"Contract Already Exists: {hash}"); @@ -152,7 +153,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma UpdateCounter = 0, Nef = nef, Hash = hash, - Manifest = ContractManifest.Parse(manifest) + Manifest = parsedManifest }; if (!contract.Manifest.IsValid(hash)) throw new InvalidOperationException($"Invalid Manifest Hash: {hash}"); diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 1f2443b79c..f887e63d0e 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -55,7 +55,7 @@ protected NativeContract() Script = script }; this.Nef.CheckSum = NefFile.ComputeChecksum(Nef); - this.Hash = Helper.GetContractHash(UInt160.Zero, script); + this.Hash = Helper.GetContractHash(UInt160.Zero, this.Nef.CheckSum, Name); List descriptors = new List(); foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { diff --git a/tests/neo.UnitTests/SmartContract/UT_Helper.cs b/tests/neo.UnitTests/SmartContract/UT_Helper.cs index 609110999d..6f292c06e7 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Helper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Helper.cs @@ -18,8 +18,8 @@ public void TestGetContractHash() }; nef.CheckSum = NefFile.ComputeChecksum(nef); - Assert.AreEqual("0xb8e95ff7b11c427c29355e3398722d97bd2ca069", Neo.SmartContract.Helper.GetContractHash(UInt160.Zero, nef.Script).ToString()); - Assert.AreEqual("0x435c467b8e15cb9b1474ad7ee817ffdcfededef9", Neo.SmartContract.Helper.GetContractHash(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), nef.Script).ToString()); + Assert.AreEqual("0x9b9628e4f1611af90e761eea8cc21372380c74b6", Neo.SmartContract.Helper.GetContractHash(UInt160.Zero, nef.CheckSum, "").ToString()); + Assert.AreEqual("0x66eec404d86b918d084e62a29ac9990e3b6f4286", Neo.SmartContract.Helper.GetContractHash(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), nef.CheckSum, "").ToString()); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 131c3779a4..d268840d64 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -161,7 +161,7 @@ public void TestContract_Create() manifest = TestUtils.CreateDefaultManifest(); var ret = snapshot.DeployContract(UInt160.Zero, nefFile, manifest.ToJson().ToByteArray(false)); - ret.Hash.ToString().Should().Be("0x6410935f6b153eeb85f5d95d926c075b1ef51620"); + ret.Hash.ToString().Should().Be("0x18e26e71e66fb79347581771c0910df5bea9d24b"); Assert.ThrowsException(() => snapshot.DeployContract(UInt160.Zero, nefFile, manifest.ToJson().ToByteArray(false))); var state = TestUtils.GetContract(); diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 4135a53f84..c012d4917a 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -158,7 +158,7 @@ public void TestMakeScript() { byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - Assert.AreEqual("0c14000000000000000000000000000000000000000011c01f0c0962616c616e63654f660c14a282435d0ee334fecaac7f5fde46f623f24cc09a41627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c01f0c0962616c616e63654f660c143d2f0bce7a4d9bb50b95425a71f444eb3045847541627d5b52", testScript.ToHexString()); } From 8c72d54e495f345b4ae44c432d6e0f1a7ca7e531 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 20 Jan 2021 02:22:13 +0800 Subject: [PATCH 46/65] Change the price of registerCandidate (#2252) --- src/neo/SmartContract/Native/NeoToken.cs | 2 +- tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index 64971dc640..b95f8eae89 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -211,7 +211,7 @@ public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) return CalculateBonus(snapshot, state.VoteTo, state.Balance, state.BalanceHeight, end); } - [ContractMethod(0_05000000, CallFlags.WriteStates)] + [ContractMethod(1000_00000000, CallFlags.WriteStates)] private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) { if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index abac2d6db6..ccb340864f 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -939,7 +939,7 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] internal static (bool State, bool Result) Check_RegisterValidator(StoreView snapshot, byte[] pubkey, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); + new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock, 1100_00000000); engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); From d676fa7f88144aa6a1d3557b720d8c1a09b16968 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 20 Jan 2021 07:57:02 +0100 Subject: [PATCH 47/65] Optimize BinarySerializer (#2253) --- src/neo/SmartContract/BinarySerializer.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/neo/SmartContract/BinarySerializer.cs b/src/neo/SmartContract/BinarySerializer.cs index 3aa89fd1b1..806c59fd08 100644 --- a/src/neo/SmartContract/BinarySerializer.cs +++ b/src/neo/SmartContract/BinarySerializer.cs @@ -143,7 +143,7 @@ public static byte[] Serialize(StackItem item, uint maxSize) public static void Serialize(BinaryWriter writer, StackItem item, uint maxSize) { - List serialized = new List(); + HashSet serialized = new HashSet(ReferenceEqualityComparer.Instance); Stack unserialized = new Stack(); unserialized.Push(item); while (unserialized.Count > 0) @@ -163,17 +163,15 @@ public static void Serialize(BinaryWriter writer, StackItem item, uint maxSize) writer.WriteVarBytes(item.GetSpan()); break; case Array array: - if (serialized.Any(p => ReferenceEquals(p, array))) + if (!serialized.Add(array)) throw new NotSupportedException(); - serialized.Add(array); writer.WriteVarInt(array.Count); for (int i = array.Count - 1; i >= 0; i--) unserialized.Push(array[i]); break; case Map map: - if (serialized.Any(p => ReferenceEquals(p, map))) + if (!serialized.Add(map)) throw new NotSupportedException(); - serialized.Add(map); writer.WriteVarInt(map.Count); foreach (var pair in map.Reverse()) { From 7982856209bcf3a7adf198da5cd840e7bbe0ad15 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 21 Jan 2021 14:08:23 +0800 Subject: [PATCH 48/65] Move everything into LedgerContract (#2215) --- src/neo/IO/Caching/CloneCache.cs | 53 --- src/neo/IO/Caching/CloneMetaCache.cs | 28 -- src/neo/IO/Caching/MetaDataCache.cs | 61 ---- src/neo/IO/ICloneable.cs | 8 - src/neo/IO/SerializableWrapper.cs | 67 ---- src/neo/Ledger/Blockchain.cs | 168 ++------- src/neo/Ledger/HashIndexState.cs | 47 --- src/neo/Ledger/HeaderHashList.cs | 35 -- src/neo/Ledger/MemoryPool.cs | 21 +- src/neo/Ledger/TransactionState.cs | 50 --- .../Ledger/TransactionVerificationContext.cs | 2 +- src/neo/Network/P2P/Payloads/Block.cs | 48 +-- src/neo/Network/P2P/Payloads/BlockBase.cs | 19 +- .../Network/P2P/Payloads/ExtensiblePayload.cs | 8 +- src/neo/Network/P2P/Payloads/Header.cs | 16 - .../P2P/Payloads/HighPriorityAttribute.cs | 2 +- src/neo/Network/P2P/Payloads/IInventory.cs | 2 +- src/neo/Network/P2P/Payloads/IVerifiable.cs | 2 +- .../Network/P2P/Payloads/OracleResponse.cs | 6 +- src/neo/Network/P2P/Payloads/Transaction.cs | 11 +- .../P2P/Payloads/TransactionAttribute.cs | 2 +- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 59 ++-- src/neo/Network/P2P/TaskManager.cs | 5 +- src/neo/Persistence/ClonedCache.cs | 51 +++ src/neo/Persistence/ClonedView.cs | 26 -- .../{IO/Caching => Persistence}/DataCache.cs | 76 ++-- src/neo/Persistence/IReadOnlyStore.cs | 6 +- src/neo/Persistence/ISnapshot.cs | 4 +- src/neo/Persistence/IStore.cs | 9 +- src/neo/Persistence/MemorySnapshot.cs | 45 ++- src/neo/Persistence/MemoryStore.cs | 29 +- src/neo/Persistence/Prefixes.cs | 14 - src/neo/Persistence/ReadOnlyView.cs | 32 -- .../Caching => Persistence}/SeekDirection.cs | 2 +- src/neo/Persistence/SnapshotCache.cs | 66 ++++ src/neo/Persistence/SnapshotView.cs | 44 --- src/neo/Persistence/StoreDataCache.cs | 59 ---- src/neo/Persistence/StoreMetaDataCache.cs | 37 -- src/neo/Persistence/StoreView.cs | 70 ---- .../{IO/Caching => Persistence}/TrackState.cs | 2 +- src/neo/Plugins/IApplicationEngineProvider.cs | 2 +- src/neo/Plugins/IPersistencePlugin.cs | 4 +- .../ApplicationEngine.Blockchain.cs | 95 ----- .../ApplicationEngine.Contract.cs | 2 +- .../ApplicationEngine.Runtime.cs | 2 +- .../ApplicationEngine.Storage.cs | 13 +- src/neo/SmartContract/ApplicationEngine.cs | 19 +- src/neo/SmartContract/Contract.cs | 5 + .../ContractParametersContext.cs | 2 +- src/neo/SmartContract/Helper.cs | 6 +- .../Native/ContractManagement.cs | 37 +- .../Native/ContractMethodMetadata.cs | 2 +- src/neo/SmartContract/Native/FungibleToken.cs | 25 +- src/neo/SmartContract/Native/GasToken.cs | 2 +- .../SmartContract/Native/HashIndexState.cs | 24 ++ .../SmartContract/Native/LedgerContract.cs | 205 +++++++++++ src/neo/SmartContract/Native/NameService.cs | 63 ++-- .../SmartContract/Native/NativeContract.cs | 1 + src/neo/SmartContract/Native/NeoToken.cs | 85 +++-- .../SmartContract/Native/NonfungibleToken.cs | 47 ++- .../SmartContract/Native/OracleContract.cs | 31 +- .../SmartContract/Native/PolicyContract.cs | 49 ++- .../SmartContract/Native/RoleManagement.cs | 12 +- .../SmartContract/Native/TransactionState.cs | 29 ++ .../Native}/TrimmedBlock.cs | 82 ++--- .../{Ledger => SmartContract}/StorageFlags.cs | 2 +- .../{Ledger => SmartContract}/StorageItem.cs | 9 +- .../{Ledger => SmartContract}/StorageKey.cs | 2 +- src/neo/Wallets/AssetDescriptor.cs | 2 +- src/neo/Wallets/Wallet.cs | 14 +- .../Extensions/NativeContractExtensions.cs | 18 +- .../Nep17NativeContractExtensions.cs | 12 +- .../neo.UnitTests/IO/Caching/UT_CloneCache.cs | 77 ++-- .../IO/Caching/UT_CloneMetaCache.cs | 48 --- .../neo.UnitTests/IO/Caching/UT_DataCache.cs | 187 +++++----- .../IO/Caching/UT_MetaDataCache.cs | 76 ---- .../IO/UT_SerializableWrapper.cs | 60 ---- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 74 +--- .../neo.UnitTests/Ledger/UT_HashIndexState.cs | 44 +-- .../neo.UnitTests/Ledger/UT_HeaderHashList.cs | 59 ---- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 16 +- tests/neo.UnitTests/Ledger/UT_StorageItem.cs | 7 +- tests/neo.UnitTests/Ledger/UT_StorageKey.cs | 2 +- .../Ledger/UT_TransactionState.cs | 52 +-- .../UT_TransactionVerificationContext.cs | 4 +- tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs | 44 +-- .../Network/P2P/Payloads/UT_Block.cs | 27 +- .../Network/P2P/Payloads/UT_Header.cs | 32 +- .../Network/P2P/Payloads/UT_Transaction.cs | 27 +- .../Persistence/UT_MemoryStore.cs | 18 +- .../Persistence/UT_ReadOnlyView.cs | 33 -- .../SmartContract/Native/UT_FungibleToken.cs | 5 +- .../SmartContract/Native/UT_GasToken.cs | 20 +- .../SmartContract/Native/UT_NameService.cs | 24 +- .../SmartContract/Native/UT_NeoToken.cs | 328 ++++++++++-------- .../SmartContract/Native/UT_PolicyContract.cs | 29 +- .../SmartContract/Native/UT_RoleManagement.cs | 6 +- .../UT_ApplicationEngineProvider.cs | 4 +- .../SmartContract/UT_InteropPrices.cs | 8 +- .../SmartContract/UT_InteropService.NEO.cs | 16 +- .../SmartContract/UT_InteropService.cs | 67 ++-- .../SmartContract/UT_SmartContractHelper.cs | 74 +++- .../SmartContract/UT_Syscalls.cs | 32 +- tests/neo.UnitTests/TestUtils.cs | 15 +- tests/neo.UnitTests/TestVerifiable.cs | 2 +- tests/neo.UnitTests/UT_DataCache.cs | 14 +- tests/neo.UnitTests/VM/UT_Helper.cs | 2 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 20 +- 108 files changed, 1445 insertions(+), 2303 deletions(-) delete mode 100644 src/neo/IO/Caching/CloneCache.cs delete mode 100644 src/neo/IO/Caching/CloneMetaCache.cs delete mode 100644 src/neo/IO/Caching/MetaDataCache.cs delete mode 100644 src/neo/IO/ICloneable.cs delete mode 100644 src/neo/IO/SerializableWrapper.cs delete mode 100644 src/neo/Ledger/HashIndexState.cs delete mode 100644 src/neo/Ledger/HeaderHashList.cs delete mode 100644 src/neo/Ledger/TransactionState.cs create mode 100644 src/neo/Persistence/ClonedCache.cs delete mode 100644 src/neo/Persistence/ClonedView.cs rename src/neo/{IO/Caching => Persistence}/DataCache.cs (82%) delete mode 100644 src/neo/Persistence/Prefixes.cs delete mode 100644 src/neo/Persistence/ReadOnlyView.cs rename src/neo/{IO/Caching => Persistence}/SeekDirection.cs (78%) create mode 100644 src/neo/Persistence/SnapshotCache.cs delete mode 100644 src/neo/Persistence/SnapshotView.cs delete mode 100644 src/neo/Persistence/StoreDataCache.cs delete mode 100644 src/neo/Persistence/StoreMetaDataCache.cs delete mode 100644 src/neo/Persistence/StoreView.cs rename src/neo/{IO/Caching => Persistence}/TrackState.cs (81%) delete mode 100644 src/neo/SmartContract/ApplicationEngine.Blockchain.cs create mode 100644 src/neo/SmartContract/Native/HashIndexState.cs create mode 100644 src/neo/SmartContract/Native/LedgerContract.cs create mode 100644 src/neo/SmartContract/Native/TransactionState.cs rename src/neo/{Ledger => SmartContract/Native}/TrimmedBlock.cs (52%) rename src/neo/{Ledger => SmartContract}/StorageFlags.cs (81%) rename src/neo/{Ledger => SmartContract}/StorageItem.cs (92%) rename src/neo/{Ledger => SmartContract}/StorageKey.cs (98%) delete mode 100644 tests/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs delete mode 100644 tests/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs delete mode 100644 tests/neo.UnitTests/IO/UT_SerializableWrapper.cs delete mode 100644 tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs delete mode 100644 tests/neo.UnitTests/Persistence/UT_ReadOnlyView.cs diff --git a/src/neo/IO/Caching/CloneCache.cs b/src/neo/IO/Caching/CloneCache.cs deleted file mode 100644 index 45da3cc7da..0000000000 --- a/src/neo/IO/Caching/CloneCache.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Neo.IO.Caching -{ - internal class CloneCache : DataCache - where TKey : IEquatable, ISerializable - where TValue : class, ICloneable, ISerializable, new() - { - private readonly DataCache innerCache; - - public CloneCache(DataCache innerCache) - { - this.innerCache = innerCache; - } - - protected override void AddInternal(TKey key, TValue value) - { - innerCache.Add(key, value); - } - - protected override void DeleteInternal(TKey key) - { - innerCache.Delete(key); - } - - protected override bool ContainsInternal(TKey key) - { - return innerCache.Contains(key); - } - - protected override TValue GetInternal(TKey key) - { - return innerCache[key].Clone(); - } - - protected override IEnumerable<(TKey, TValue)> SeekInternal(byte[] keyOrPreifx, SeekDirection direction) - { - foreach (var (key, value) in innerCache.Seek(keyOrPreifx, direction)) - yield return (key, value.Clone()); - } - - protected override TValue TryGetInternal(TKey key) - { - return innerCache.TryGet(key)?.Clone(); - } - - protected override void UpdateInternal(TKey key, TValue value) - { - innerCache.GetAndChange(key).FromReplica(value); - } - } -} diff --git a/src/neo/IO/Caching/CloneMetaCache.cs b/src/neo/IO/Caching/CloneMetaCache.cs deleted file mode 100644 index 3cb0d83201..0000000000 --- a/src/neo/IO/Caching/CloneMetaCache.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace Neo.IO.Caching -{ - internal class CloneMetaCache : MetaDataCache - where T : class, ICloneable, ISerializable, new() - { - private readonly MetaDataCache innerCache; - - public CloneMetaCache(MetaDataCache innerCache) - : base(null) - { - this.innerCache = innerCache; - } - - protected override void AddInternal(T item) - { - } - - protected override T TryGetInternal() - { - return innerCache.Get().Clone(); - } - - protected override void UpdateInternal(T item) - { - innerCache.GetAndChange().FromReplica(item); - } - } -} diff --git a/src/neo/IO/Caching/MetaDataCache.cs b/src/neo/IO/Caching/MetaDataCache.cs deleted file mode 100644 index f0c40cc59a..0000000000 --- a/src/neo/IO/Caching/MetaDataCache.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; - -namespace Neo.IO.Caching -{ - public abstract class MetaDataCache - where T : class, ICloneable, ISerializable, new() - { - private T Item; - private TrackState State; - private readonly Func factory; - - protected abstract void AddInternal(T item); - protected abstract T TryGetInternal(); - protected abstract void UpdateInternal(T item); - - protected MetaDataCache(Func factory) - { - this.factory = factory; - } - - public void Commit() - { - switch (State) - { - case TrackState.Added: - AddInternal(Item); - break; - case TrackState.Changed: - UpdateInternal(Item); - break; - } - } - - public MetaDataCache CreateSnapshot() - { - return new CloneMetaCache(this); - } - - public T Get() - { - if (Item == null) - { - Item = TryGetInternal(); - } - if (Item == null) - { - Item = factory?.Invoke() ?? new T(); - State = TrackState.Added; - } - return Item; - } - - public T GetAndChange() - { - T item = Get(); - if (State == TrackState.None) - State = TrackState.Changed; - return item; - } - } -} diff --git a/src/neo/IO/ICloneable.cs b/src/neo/IO/ICloneable.cs deleted file mode 100644 index 83b4d77725..0000000000 --- a/src/neo/IO/ICloneable.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Neo.IO -{ - public interface ICloneable - { - T Clone(); - void FromReplica(T replica); - } -} diff --git a/src/neo/IO/SerializableWrapper.cs b/src/neo/IO/SerializableWrapper.cs deleted file mode 100644 index 5533a44171..0000000000 --- a/src/neo/IO/SerializableWrapper.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.IO; - -namespace Neo.IO -{ - public class SerializableWrapper : IEquatable, IEquatable>, ISerializable - where T : unmanaged - { - private static unsafe readonly int ValueSize = sizeof(T); - private T value; - - public SerializableWrapper() - { - } - - private SerializableWrapper(T value) - { - this.value = value; - } - - public int Size => ValueSize; - - public unsafe void Deserialize(BinaryReader reader) - { - fixed (T* p = &value) - { - Span buffer = new Span(p, ValueSize); - int i = 0; - while (i < ValueSize) - { - int count = reader.Read(buffer[i..]); - if (count == 0) throw new FormatException(); - i += count; - } - } - } - - public bool Equals(T other) - { - return value.Equals(other); - } - - public bool Equals(SerializableWrapper other) - { - return value.Equals(other.value); - } - - public unsafe void Serialize(BinaryWriter writer) - { - fixed (T* p = &value) - { - ReadOnlySpan buffer = new ReadOnlySpan(p, ValueSize); - writer.Write(buffer); - } - } - - public static implicit operator SerializableWrapper(T value) - { - return new SerializableWrapper(value); - } - - public static implicit operator T(SerializableWrapper wrapper) - { - return wrapper.value; - } - } -} diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index bf26f6c70d..e7bb4bcfba 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -43,7 +43,7 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu PrevHash = UInt256.Zero, Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestampMS(), Index = 0, - NextConsensus = GetConsensusAddress(StandbyValidators), + NextConsensus = Contract.GetBFTAddress(StandbyValidators), Witness = new Witness { InvocationScript = Array.Empty(), @@ -62,21 +62,17 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu private static readonly object lockObj = new object(); private readonly NeoSystem system; private readonly IActorRef txrouter; - private readonly List header_index = new List(); - private uint stored_header_count = 0; private readonly ConcurrentDictionary block_cache = new ConcurrentDictionary(); private readonly Dictionary block_cache_unverified = new Dictionary(); internal readonly RelayCache RelayCache = new RelayCache(100); - private SnapshotView currentSnapshot; + private SnapshotCache currentSnapshot; private ImmutableHashSet extensibleWitnessWhiteList; public IStore Store { get; } - public ReadOnlyView View { get; } + public DataCache View => new SnapshotCache(Store); public MemoryPool MemPool { get; } - public uint Height => currentSnapshot.Height; - public uint HeaderHeight => currentSnapshot.HeaderHeight; - public UInt256 CurrentBlockHash => currentSnapshot.CurrentBlockHash; - public UInt256 CurrentHeaderHash => currentSnapshot.CurrentHeaderHash; + public uint Height => NativeContract.Ledger.CurrentIndex(currentSnapshot); + public UInt256 CurrentBlockHash => NativeContract.Ledger.CurrentHash(currentSnapshot); private static Blockchain singleton; public static Blockchain Singleton @@ -110,31 +106,11 @@ public Blockchain(NeoSystem system, IStore store) this.txrouter = Context.ActorOf(TransactionRouter.Props(system)); this.MemPool = new MemoryPool(system, ProtocolSettings.Default.MemoryPoolMaxTransactions); this.Store = store; - this.View = new ReadOnlyView(store); lock (lockObj) { if (singleton != null) throw new InvalidOperationException(); - header_index.AddRange(View.HeaderHashList.Find().OrderBy(p => (uint)p.Key).SelectMany(p => p.Value.Hashes)); - stored_header_count += (uint)header_index.Count; - if (stored_header_count == 0) - { - header_index.AddRange(View.Blocks.Find().OrderBy(p => p.Value.Index).Select(p => p.Key)); - } - else - { - HashIndexState hashIndex = View.HeaderHashIndex.Get(); - if (hashIndex.Index >= stored_header_count) - { - DataCache cache = View.Blocks; - for (UInt256 hash = hashIndex.Hash; hash != header_index[(int)stored_header_count - 1];) - { - header_index.Insert((int)stored_header_count, hash); - hash = cache[hash].PrevHash; - } - } - } - if (header_index.Count == 0) + if (!NativeContract.Ledger.Initialized(View)) { Persist(GenesisBlock); } @@ -147,76 +123,15 @@ public Blockchain(NeoSystem system, IStore store) } } - public bool ContainsBlock(UInt256 hash) - { - if (block_cache.ContainsKey(hash)) return true; - return View.ContainsBlock(hash); - } - - public bool ContainsTransaction(UInt256 hash) + private bool ContainsTransaction(UInt256 hash) { if (MemPool.ContainsKey(hash)) return true; - return View.ContainsTransaction(hash); - } - - public Block GetBlock(uint index) - { - if (index == 0) return GenesisBlock; - UInt256 hash = GetBlockHash(index); - if (hash == null) return null; - return GetBlock(hash); + return NativeContract.Ledger.ContainsTransaction(View, hash); } - public Block GetBlock(UInt256 hash) + public SnapshotCache GetSnapshot() { - if (block_cache.TryGetValue(hash, out Block block)) - return block; - return View.GetBlock(hash); - } - - public UInt256 GetBlockHash(uint index) - { - if (header_index.Count <= index) return null; - return header_index[(int)index]; - } - - public static UInt160 GetConsensusAddress(ECPoint[] validators) - { - return Contract.CreateMultiSigRedeemScript(validators.Length - (validators.Length - 1) / 3, validators).ToScriptHash(); - } - - public Header GetHeader(uint index) - { - if (index == 0) return GenesisBlock.Header; - UInt256 hash = GetBlockHash(index); - if (hash == null) return null; - return GetHeader(hash); - } - - public Header GetHeader(UInt256 hash) - { - if (block_cache.TryGetValue(hash, out Block block)) - return block.Header; - return View.GetHeader(hash); - } - - public UInt256 GetNextBlockHash(UInt256 hash) - { - Header header = GetHeader(hash); - if (header == null) return null; - return GetBlockHash(header.Index + 1); - } - - public SnapshotView GetSnapshot() - { - return new SnapshotView(Store); - } - - public Transaction GetTransaction(UInt256 hash) - { - if (MemPool.TryGetValue(hash, out Transaction transaction)) - return transaction; - return View.GetTransaction(hash); + return new SnapshotCache(Store.GetSnapshot()); } private void OnImport(IEnumerable blocks, bool verify) @@ -229,7 +144,6 @@ private void OnImport(IEnumerable blocks, bool verify) if (verify && !block.Verify(currentSnapshot)) throw new InvalidOperationException(); Persist(block); - SaveHeaderHashList(); } Sender.Tell(new ImportCompleted()); } @@ -271,7 +185,7 @@ private void OnFillMemoryPool(IEnumerable transactions) // Add the transactions to the memory pool foreach (var tx in transactions) { - if (View.ContainsTransaction(tx.Hash)) + if (NativeContract.Ledger.ContainsTransaction(View, tx.Hash)) continue; // First remove the tx if it is unverified in the pool. MemPool.TryRemoveUnVerified(tx.Hash, out _); @@ -314,7 +228,6 @@ private VerifyResult OnNewBlock(Block block) block_cache.TryAdd(block.Hash, block); block_cache_unverified.Remove(block.Index); Persist(block); - SaveHeaderHashList(); if (block_cache_unverified.TryGetValue(Height + 1, out var unverifiedBlocks)) { foreach (var unverifiedBlock in unverifiedBlocks.Blocks) @@ -387,13 +300,8 @@ private void OnTransaction(Transaction tx) private void Persist(Block block) { - using (SnapshotView snapshot = GetSnapshot()) + using (SnapshotCache snapshot = GetSnapshot()) { - if (block.Index == header_index.Count) - { - header_index.Add(block.Hash); - snapshot.HeaderHashIndex.GetAndChange().Set(block); - } List all_application_executed = new List(); using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, block)) { @@ -403,38 +311,26 @@ private void Persist(Block block) Context.System.EventStream.Publish(application_executed); all_application_executed.Add(application_executed); } - snapshot.Blocks.Add(block.Hash, block.Trim()); - StoreView clonedSnapshot = snapshot.Clone(); + DataCache clonedSnapshot = snapshot.CreateSnapshot(); // Warning: Do not write into variable snapshot directly. Write into variable clonedSnapshot and commit instead. foreach (Transaction tx in block.Transactions) { - var state = new TransactionState - { - BlockIndex = block.Index, - Transaction = tx - }; - - clonedSnapshot.Transactions.Add(tx.Hash, state); - clonedSnapshot.Transactions.Commit(); - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, block, tx.SystemFee)) { engine.LoadScript(tx.Script); - state.VMState = engine.Execute(); - if (state.VMState == VMState.HALT) + if (engine.Execute() == VMState.HALT) { clonedSnapshot.Commit(); } else { - clonedSnapshot = snapshot.Clone(); + clonedSnapshot = snapshot.CreateSnapshot(); } ApplicationExecuted application_executed = new ApplicationExecuted(engine); Context.System.EventStream.Publish(application_executed); all_application_executed.Add(application_executed); } } - snapshot.BlockHashIndex.GetAndChange().Set(block); using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, block)) { engine.LoadScript(postPersistScript); @@ -483,30 +379,6 @@ public static Props Props(NeoSystem system, IStore store) return Akka.Actor.Props.Create(() => new Blockchain(system, store)).WithMailbox("blockchain-mailbox"); } - private void SaveHeaderHashList(SnapshotView snapshot = null) - { - if ((header_index.Count - stored_header_count < 2000)) - return; - bool snapshot_created = snapshot == null; - if (snapshot_created) snapshot = GetSnapshot(); - try - { - while (header_index.Count - stored_header_count >= 2000) - { - snapshot.HeaderHashList.Add(stored_header_count, new HeaderHashList - { - Hashes = header_index.Skip((int)stored_header_count).Take(2000).ToArray() - }); - stored_header_count += 2000; - } - if (snapshot_created) snapshot.Commit(); - } - finally - { - if (snapshot_created) snapshot.Dispose(); - } - } - private void SendRelayResult(IInventory inventory, VerifyResult result) { RelayResult rr = new RelayResult @@ -524,18 +396,18 @@ private void UpdateCurrentSnapshot() var builder = ImmutableHashSet.CreateBuilder(); builder.Add(NativeContract.NEO.GetCommitteeAddress(currentSnapshot)); var validators = NativeContract.NEO.GetNextBlockValidators(currentSnapshot); - builder.Add(GetConsensusAddress(validators)); + builder.Add(Contract.GetBFTAddress(validators)); builder.UnionWith(validators.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); - var oracles = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.Oracle, currentSnapshot.Height); + var oracles = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.Oracle, Height); if (oracles.Length > 0) { - builder.Add(GetConsensusAddress(oracles)); + builder.Add(Contract.GetBFTAddress(oracles)); builder.UnionWith(oracles.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); } - var stateValidators = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.StateValidator, currentSnapshot.Height); + var stateValidators = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.StateValidator, Height); if (stateValidators.Length > 0) { - builder.Add(GetConsensusAddress(stateValidators)); + builder.Add(Contract.GetBFTAddress(stateValidators)); builder.UnionWith(stateValidators.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); } extensibleWitnessWhiteList = builder.ToImmutable(); diff --git a/src/neo/Ledger/HashIndexState.cs b/src/neo/Ledger/HashIndexState.cs deleted file mode 100644 index fb7439a252..0000000000 --- a/src/neo/Ledger/HashIndexState.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Neo.IO; -using Neo.Network.P2P.Payloads; -using System.IO; - -namespace Neo.Ledger -{ - public class HashIndexState : ICloneable, ISerializable - { - public UInt256 Hash = UInt256.Zero; - public uint Index = uint.MaxValue; - - int ISerializable.Size => Hash.Size + sizeof(uint); - - HashIndexState ICloneable.Clone() - { - return new HashIndexState - { - Hash = Hash, - Index = Index - }; - } - - void ISerializable.Deserialize(BinaryReader reader) - { - Hash = reader.ReadSerializable(); - Index = reader.ReadUInt32(); - } - - void ICloneable.FromReplica(HashIndexState replica) - { - Hash = replica.Hash; - Index = replica.Index; - } - - void ISerializable.Serialize(BinaryWriter writer) - { - writer.Write(Hash); - writer.Write(Index); - } - - internal void Set(BlockBase block) - { - Hash = block.Hash; - Index = block.Index; - } - } -} diff --git a/src/neo/Ledger/HeaderHashList.cs b/src/neo/Ledger/HeaderHashList.cs deleted file mode 100644 index 928a26e5d2..0000000000 --- a/src/neo/Ledger/HeaderHashList.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Neo.IO; -using System.IO; - -namespace Neo.Ledger -{ - public class HeaderHashList : ICloneable, ISerializable - { - public UInt256[] Hashes; - - int ISerializable.Size => Hashes.GetVarSize(); - - HeaderHashList ICloneable.Clone() - { - return new HeaderHashList - { - Hashes = Hashes - }; - } - - void ISerializable.Deserialize(BinaryReader reader) - { - Hashes = reader.ReadSerializableArray(); - } - - void ICloneable.FromReplica(HeaderHashList replica) - { - Hashes = replica.Hashes; - } - - void ISerializable.Serialize(BinaryWriter writer) - { - writer.Write(Hashes); - } - } -} diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index 74697d041f..bc15638fef 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -1,4 +1,3 @@ -using Akka.Actor; using Akka.Util.Internal; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; @@ -104,7 +103,7 @@ public MemoryPool(NeoSystem system, int capacity) Capacity = capacity; } - internal void LoadPolicy(StoreView snapshot) + internal void LoadPolicy(DataCache snapshot) { _maxTxPerBlock = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); } @@ -206,7 +205,7 @@ public IEnumerable GetSortedVerifiedTransactions() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private PoolItem GetLowestFeeTransaction(SortedSet verifiedTxSorted, + private static PoolItem GetLowestFeeTransaction(SortedSet verifiedTxSorted, SortedSet unverifiedTxSorted, out SortedSet sortedPool) { PoolItem minItem = unverifiedTxSorted.Min; @@ -256,7 +255,7 @@ internal bool CanTransactionFitInPool(Transaction tx) /// /// /// - internal VerifyResult TryAdd(Transaction tx, StoreView snapshot) + internal VerifyResult TryAdd(Transaction tx, DataCache snapshot) { var poolItem = new PoolItem(tx); @@ -349,7 +348,7 @@ internal void InvalidateVerifiedTransactions() } // Note: this must only be called from a single thread (the Blockchain actor) - internal void UpdatePoolForBlockPersisted(Block block, StoreView snapshot) + internal void UpdatePoolForBlockPersisted(Block block, DataCache snapshot) { LoadPolicy(snapshot); @@ -371,11 +370,6 @@ internal void UpdatePoolForBlockPersisted(Block block, StoreView snapshot) _txRwLock.ExitWriteLock(); } - // If we know about headers of future blocks, no point in verifying transactions from the unverified tx pool - // until we get caught up. - if (block.Index > 0 && block.Index < Blockchain.Singleton.HeaderHeight) - return; - ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, _maxTxPerBlock, MaxMillisecondsToReverifyTx, snapshot); } @@ -394,7 +388,7 @@ internal void InvalidateAllTransactions() } private int ReverifyTransactions(SortedSet verifiedSortedTxPool, - SortedSet unverifiedSortedTxPool, int count, double millisecondsTimeout, StoreView snapshot) + SortedSet unverifiedSortedTxPool, int count, double millisecondsTimeout, DataCache snapshot) { DateTime reverifyCutOffTimeStamp = TimeProvider.Current.UtcNow.AddMilliseconds(millisecondsTimeout); List reverifiedItems = new List(count); @@ -470,11 +464,8 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, /// Max transactions to reverify, the value passed can be >=1 /// The snapshot to use for verifying. /// true if more unsorted messages exist, otherwise false - internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, StoreView snapshot) + internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, DataCache snapshot) { - if (Blockchain.Singleton.Height < Blockchain.Singleton.HeaderHeight) - return false; - if (_unverifiedSortedTransactions.Count > 0) { int verifyCount = _sortedTransactions.Count > _maxTxPerBlock ? 1 : maxToVerify; diff --git a/src/neo/Ledger/TransactionState.cs b/src/neo/Ledger/TransactionState.cs deleted file mode 100644 index c8a479424a..0000000000 --- a/src/neo/Ledger/TransactionState.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Neo.IO; -using Neo.Network.P2P.Payloads; -using Neo.VM; -using System.IO; - -namespace Neo.Ledger -{ - public class TransactionState : ICloneable, ISerializable - { - public uint BlockIndex; - public VMState VMState; - public Transaction Transaction; - - int ISerializable.Size => - sizeof(uint) + // BlockIndex - sizeof(VMState) + // VMState - Transaction.Size; // Transaction - - TransactionState ICloneable.Clone() - { - return new TransactionState - { - BlockIndex = BlockIndex, - VMState = VMState, - Transaction = Transaction - }; - } - - void ISerializable.Deserialize(BinaryReader reader) - { - BlockIndex = reader.ReadUInt32(); - VMState = (VMState)reader.ReadByte(); - Transaction = reader.ReadSerializable(); - } - - void ICloneable.FromReplica(TransactionState replica) - { - BlockIndex = replica.BlockIndex; - VMState = replica.VMState; - Transaction = replica.Transaction; - } - - void ISerializable.Serialize(BinaryWriter writer) - { - writer.Write(BlockIndex); - writer.Write((byte)VMState); - writer.Write(Transaction); - } - } -} diff --git a/src/neo/Ledger/TransactionVerificationContext.cs b/src/neo/Ledger/TransactionVerificationContext.cs index e291974137..cfa9dc15cd 100644 --- a/src/neo/Ledger/TransactionVerificationContext.cs +++ b/src/neo/Ledger/TransactionVerificationContext.cs @@ -29,7 +29,7 @@ public void AddTransaction(Transaction tx) senderFee.Add(tx.Sender, tx.SystemFee + tx.NetworkFee); } - public bool CheckTransaction(Transaction tx, StoreView snapshot) + public bool CheckTransaction(Transaction tx, DataCache snapshot) { BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, tx.Sender); senderFee.TryGetValue(tx.Sender, out var totalSenderFeeFromPool); diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index bbea8db9cc..73b1ff97bd 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -1,19 +1,14 @@ using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; -using Neo.Ledger; -using Neo.SmartContract; -using Neo.VM; -using Neo.VM.Types; using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Array = Neo.VM.Types.Array; namespace Neo.Network.P2P.Payloads { - public class Block : BlockBase, IInventory, IEquatable, IInteroperable + public class Block : BlockBase, IInventory, IEquatable { public const int MaxContentsPerBlock = ushort.MaxValue; public const int MaxTransactionsPerBlock = MaxContentsPerBlock - 1; @@ -81,11 +76,6 @@ public override bool Equals(object obj) return Equals(obj as Block); } - void IInteroperable.FromStackItem(StackItem stackItem) - { - throw new NotSupportedException(); - } - public override int GetHashCode() { return Hash.GetHashCode(); @@ -112,41 +102,5 @@ public override JObject ToJson() json["tx"] = Transactions.Select(p => p.ToJson()).ToArray(); return json; } - - public TrimmedBlock Trim() - { - return new TrimmedBlock - { - Version = Version, - PrevHash = PrevHash, - MerkleRoot = MerkleRoot, - Timestamp = Timestamp, - Index = Index, - NextConsensus = NextConsensus, - Witness = Witness, - Hashes = Transactions.Select(p => p.Hash).Prepend(ConsensusData.Hash).ToArray(), - ConsensusData = ConsensusData - }; - } - - public StackItem ToStackItem(ReferenceCounter referenceCounter) - { - return new Array(referenceCounter, new StackItem[] - { - // Computed properties - Hash.ToArray(), - - // BlockBase properties - Version, - PrevHash.ToArray(), - MerkleRoot.ToArray(), - Timestamp, - Index, - NextConsensus.ToArray(), - - // Block properties - Transactions.Length - }); - } } } diff --git a/src/neo/Network/P2P/Payloads/BlockBase.cs b/src/neo/Network/P2P/Payloads/BlockBase.cs index 51dfd3377e..08b2657295 100644 --- a/src/neo/Network/P2P/Payloads/BlockBase.cs +++ b/src/neo/Network/P2P/Payloads/BlockBase.cs @@ -2,6 +2,7 @@ using Neo.IO.Json; using Neo.Persistence; using Neo.SmartContract; +using Neo.SmartContract.Native; using Neo.Wallets; using System; using System.IO; @@ -72,12 +73,12 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) NextConsensus = reader.ReadSerializable(); } - UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot) + UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) { if (PrevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; - Header prev_header = snapshot.GetHeader(PrevHash); - if (prev_header == null) throw new InvalidOperationException(); - return new[] { prev_header.NextConsensus }; + TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); + if (prev is null) throw new InvalidOperationException(); + return new[] { prev.NextConsensus }; } public virtual void Serialize(BinaryWriter writer) @@ -111,12 +112,12 @@ public virtual JObject ToJson() return json; } - public virtual bool Verify(StoreView snapshot) + public virtual bool Verify(DataCache snapshot) { - Header prev_header = snapshot.GetHeader(PrevHash); - if (prev_header == null) return false; - if (prev_header.Index + 1 != Index) return false; - if (prev_header.Timestamp >= Timestamp) return false; + TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); + if (prev is null) return false; + if (prev.Index + 1 != Index) return false; + if (prev.Timestamp >= Timestamp) return false; if (!this.VerifyWitnesses(snapshot, 1_00000000)) return false; return true; } diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs index c5414770b2..4e405d4814 100644 --- a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -2,6 +2,7 @@ using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; +using Neo.SmartContract.Native; using System; using System.IO; @@ -69,7 +70,7 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) Data = reader.ReadVarBytes(ushort.MaxValue); } - UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot) + UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) { return new[] { Sender }; // This address should be checked by consumer } @@ -89,9 +90,10 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.WriteVarBytes(Data); } - public bool Verify(StoreView snapshot) + public bool Verify(DataCache snapshot) { - if (snapshot.Height < ValidBlockStart || snapshot.Height >= ValidBlockEnd) return false; + uint height = NativeContract.Ledger.CurrentIndex(snapshot); + if (height < ValidBlockStart || height >= ValidBlockEnd) return false; if (!Blockchain.Singleton.IsExtensibleWitnessWhiteListed(Sender)) return false; return this.VerifyWitnesses(snapshot, 0_02000000); } diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs index d227860bb0..301d1cf05b 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/neo/Network/P2P/Payloads/Header.cs @@ -1,4 +1,3 @@ -using Neo.Ledger; using System; using System.IO; @@ -36,20 +35,5 @@ public override void Serialize(BinaryWriter writer) base.Serialize(writer); writer.Write((byte)0); } - - public TrimmedBlock Trim() - { - return new TrimmedBlock - { - Version = Version, - PrevHash = PrevHash, - MerkleRoot = MerkleRoot, - Timestamp = Timestamp, - Index = Index, - NextConsensus = NextConsensus, - Witness = Witness, - Hashes = new UInt256[0] - }; - } } } diff --git a/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs b/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs index 48d4b79a95..d3077d3e88 100644 --- a/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs +++ b/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs @@ -18,7 +18,7 @@ protected override void SerializeWithoutType(BinaryWriter writer) { } - public override bool Verify(StoreView snapshot, Transaction tx) + public override bool Verify(DataCache snapshot, Transaction tx) { UInt160 committee = NativeContract.NEO.GetCommitteeAddress(snapshot); return tx.Signers.Any(p => p.Account.Equals(committee)); diff --git a/src/neo/Network/P2P/Payloads/IInventory.cs b/src/neo/Network/P2P/Payloads/IInventory.cs index 2b175647c5..49256132c6 100644 --- a/src/neo/Network/P2P/Payloads/IInventory.cs +++ b/src/neo/Network/P2P/Payloads/IInventory.cs @@ -8,6 +8,6 @@ public interface IInventory : IVerifiable InventoryType InventoryType { get; } - bool Verify(StoreView snapshot); + bool Verify(DataCache snapshot); } } diff --git a/src/neo/Network/P2P/Payloads/IVerifiable.cs b/src/neo/Network/P2P/Payloads/IVerifiable.cs index cecd0570da..3e574366ed 100644 --- a/src/neo/Network/P2P/Payloads/IVerifiable.cs +++ b/src/neo/Network/P2P/Payloads/IVerifiable.cs @@ -10,7 +10,7 @@ public interface IVerifiable : ISerializable void DeserializeUnsigned(BinaryReader reader); - UInt160[] GetScriptHashesForVerifying(StoreView snapshot); + UInt160[] GetScriptHashesForVerifying(DataCache snapshot); void SerializeUnsigned(BinaryWriter writer); } diff --git a/src/neo/Network/P2P/Payloads/OracleResponse.cs b/src/neo/Network/P2P/Payloads/OracleResponse.cs index 77ed8cf7ec..81bf449292 100644 --- a/src/neo/Network/P2P/Payloads/OracleResponse.cs +++ b/src/neo/Network/P2P/Payloads/OracleResponse.cs @@ -1,7 +1,7 @@ using Neo.IO; using Neo.IO.Json; -using Neo.Ledger; using Neo.Persistence; +using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; using System; @@ -62,14 +62,14 @@ public override JObject ToJson() return json; } - public override bool Verify(StoreView snapshot, Transaction tx) + public override bool Verify(DataCache snapshot, Transaction tx) { if (tx.Signers.Any(p => p.Scopes != WitnessScope.None)) return false; if (!tx.Script.AsSpan().SequenceEqual(FixedScript)) return false; OracleRequest request = NativeContract.Oracle.GetRequest(snapshot, Id); if (request is null) return false; if (tx.NetworkFee + tx.SystemFee != request.GasForResponse) return false; - UInt160 oracleAccount = Blockchain.GetConsensusAddress(NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.Oracle, snapshot.Height + 1)); + UInt160 oracleAccount = Contract.GetBFTAddress(NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.Oracle, NativeContract.Ledger.CurrentIndex(snapshot) + 1)); return tx.Signers.Any(p => p.Account.Equals(oracleAccount)); } } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 77252f04c6..2007c8c202 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -235,7 +235,7 @@ public override int GetHashCode() return Hash.GetHashCode(); } - public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) + public UInt160[] GetScriptHashesForVerifying(DataCache snapshot) { return Signers.Select(p => p.Account).ToArray(); } @@ -276,14 +276,15 @@ public JObject ToJson() return json; } - bool IInventory.Verify(StoreView snapshot) + bool IInventory.Verify(DataCache snapshot) { return Verify(snapshot, null) == VerifyResult.Succeed; } - public virtual VerifyResult VerifyStateDependent(StoreView snapshot, TransactionVerificationContext context) + public virtual VerifyResult VerifyStateDependent(DataCache snapshot, TransactionVerificationContext context) { - if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) + uint height = NativeContract.Ledger.CurrentIndex(snapshot); + if (ValidUntilBlock <= height || ValidUntilBlock > height + MaxValidUntilBlockIncrement) return VerifyResult.Expired; foreach (UInt160 hash in GetScriptHashesForVerifying(snapshot)) if (NativeContract.Policy.IsBlocked(snapshot, hash)) @@ -330,7 +331,7 @@ public virtual VerifyResult VerifyStateIndependent() return VerifyResult.Succeed; } - public virtual VerifyResult Verify(StoreView snapshot, TransactionVerificationContext context) + public virtual VerifyResult Verify(DataCache snapshot, TransactionVerificationContext context) { VerifyResult result = VerifyStateIndependent(); if (result != VerifyResult.Succeed) return result; diff --git a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs index 98583aa5d6..acd3238119 100644 --- a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -47,6 +47,6 @@ public void Serialize(BinaryWriter writer) protected abstract void SerializeWithoutType(BinaryWriter writer); - public virtual bool Verify(StoreView snapshot, Transaction tx) => true; + public virtual bool Verify(DataCache snapshot, Transaction tx) => true; } } diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 7d85875c56..0cb6b1afcb 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -6,6 +6,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.Plugins; +using Neo.SmartContract.Native; using System; using System.Collections; using System.Collections.Generic; @@ -169,20 +170,23 @@ private void OnGetAddrMessageReceived() /// A GetBlocksPayload including start block Hash and number of blocks requested. private void OnGetBlocksMessageReceived(GetBlocksPayload payload) { - UInt256 hash = payload.HashStart; - // The default value of payload.Count is -1 - int count = payload.Count < 0 || payload.Count > InvPayload.MaxHashesCount ? InvPayload.MaxHashesCount : payload.Count; - TrimmedBlock state = Blockchain.Singleton.View.Blocks.TryGet(hash); - if (state == null) return; List hashes = new List(); - for (uint i = 1; i <= count; i++) + using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) { - uint index = state.Index + i; - if (index > Blockchain.Singleton.Height) - break; - hash = Blockchain.Singleton.GetBlockHash(index); - if (hash == null) break; - hashes.Add(hash); + UInt256 hash = payload.HashStart; + // The default value of payload.Count is -1 + int count = payload.Count < 0 || payload.Count > InvPayload.MaxHashesCount ? InvPayload.MaxHashesCount : payload.Count; + TrimmedBlock state = NativeContract.Ledger.GetTrimmedBlock(snapshot, hash); + if (state == null) return; + for (uint i = 1; i <= count; i++) + { + uint index = state.Index + i; + if (index > Blockchain.Singleton.Height) + break; + hash = NativeContract.Ledger.GetBlockHash(snapshot, index); + if (hash == null) break; + hashes.Add(hash); + } } if (hashes.Count == 0) return; EnqueueMessage(Message.Create(MessageCommand.Inv, InvPayload.Create(InventoryType.Block, hashes.ToArray()))); @@ -193,7 +197,7 @@ private void OnGetBlockByIndexMessageReceived(GetBlockByIndexPayload payload) uint count = payload.Count == -1 ? InvPayload.MaxHashesCount : Math.Min((uint)payload.Count, InvPayload.MaxHashesCount); for (uint i = payload.IndexStart, max = payload.IndexStart + count; i < max; i++) { - Block block = Blockchain.Singleton.GetBlock(i); + Block block = NativeContract.Ledger.GetBlock(Blockchain.Singleton.View, i); if (block == null) break; @@ -223,14 +227,13 @@ private void OnGetDataMessageReceived(InvPayload payload) switch (payload.Type) { case InventoryType.TX: - Transaction tx = Blockchain.Singleton.GetTransaction(hash); - if (tx != null) + if (Blockchain.Singleton.MemPool.TryGetValue(hash, out Transaction tx)) EnqueueMessage(Message.Create(MessageCommand.Transaction, tx)); else notFound.Add(hash); break; case InventoryType.Block: - Block block = Blockchain.Singleton.GetBlock(hash); + Block block = NativeContract.Ledger.GetBlock(Blockchain.Singleton.View, hash); if (block != null) { if (bloom_filter == null) @@ -271,15 +274,17 @@ private void OnGetDataMessageReceived(InvPayload payload) private void OnGetHeadersMessageReceived(GetBlockByIndexPayload payload) { uint index = payload.IndexStart; - uint count = payload.Count == -1 ? HeadersPayload.MaxHeadersCount : (uint)payload.Count; - if (index > Blockchain.Singleton.HeaderHeight) - return; + if (index > Blockchain.Singleton.Height) return; List
headers = new List
(); - for (uint i = 0; i < count; i++) + using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) { - var header = Blockchain.Singleton.GetHeader(index + i); - if (header == null) break; - headers.Add(header); + uint count = payload.Count == -1 ? HeadersPayload.MaxHeadersCount : (uint)payload.Count; + for (uint i = 0; i < count; i++) + { + var header = NativeContract.Ledger.GetHeader(snapshot, index + i); + if (header == null) break; + headers.Add(header); + } } if (headers.Count == 0) return; EnqueueMessage(Message.Create(MessageCommand.Headers, HeadersPayload.Create(headers.ToArray()))); @@ -305,12 +310,12 @@ private void OnInvMessageReceived(InvPayload payload) switch (payload.Type) { case InventoryType.Block: - using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) - hashes = hashes.Where(p => !snapshot.ContainsBlock(p)).ToArray(); + using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) + hashes = hashes.Where(p => !NativeContract.Ledger.ContainsBlock(snapshot, p)).ToArray(); break; case InventoryType.TX: - using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) - hashes = hashes.Where(p => !snapshot.ContainsTransaction(p)).ToArray(); + using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) + hashes = hashes.Where(p => !NativeContract.Ledger.ContainsTransaction(snapshot, p)).ToArray(); break; } if (hashes.Length == 0) return; diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index ef6e09e443..1e11baf4e0 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -5,6 +5,7 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; +using Neo.SmartContract.Native; using System; using System.Collections; using System.Collections.Generic; @@ -302,9 +303,9 @@ private void RequestTasks(bool sendPing) private void SendPingMessage() { TrimmedBlock block; - using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) { - block = snapshot.Blocks[snapshot.CurrentBlockHash]; + block = NativeContract.Ledger.GetTrimmedBlock(snapshot, NativeContract.Ledger.CurrentHash(snapshot)); } foreach (KeyValuePair item in sessions) diff --git a/src/neo/Persistence/ClonedCache.cs b/src/neo/Persistence/ClonedCache.cs new file mode 100644 index 0000000000..3fc52bc2ab --- /dev/null +++ b/src/neo/Persistence/ClonedCache.cs @@ -0,0 +1,51 @@ +using Neo.SmartContract; +using System.Collections.Generic; + +namespace Neo.Persistence +{ + class ClonedCache : DataCache + { + private readonly DataCache innerCache; + + public ClonedCache(DataCache innerCache) + { + this.innerCache = innerCache; + } + + protected override void AddInternal(StorageKey key, StorageItem value) + { + innerCache.Add(key, value); + } + + protected override void DeleteInternal(StorageKey key) + { + innerCache.Delete(key); + } + + protected override bool ContainsInternal(StorageKey key) + { + return innerCache.Contains(key); + } + + protected override StorageItem GetInternal(StorageKey key) + { + return innerCache[key].Clone(); + } + + protected override IEnumerable<(StorageKey, StorageItem)> SeekInternal(byte[] keyOrPreifx, SeekDirection direction) + { + foreach (var (key, value) in innerCache.Seek(keyOrPreifx, direction)) + yield return (key, value.Clone()); + } + + protected override StorageItem TryGetInternal(StorageKey key) + { + return innerCache.TryGet(key)?.Clone(); + } + + protected override void UpdateInternal(StorageKey key, StorageItem value) + { + innerCache.GetAndChange(key).FromReplica(value); + } + } +} diff --git a/src/neo/Persistence/ClonedView.cs b/src/neo/Persistence/ClonedView.cs deleted file mode 100644 index 3b9b8017e6..0000000000 --- a/src/neo/Persistence/ClonedView.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using Neo.Ledger; - -namespace Neo.Persistence -{ - internal class ClonedView : StoreView - { - public override DataCache Blocks { get; } - public override DataCache Transactions { get; } - public override DataCache Storages { get; } - public override DataCache, HeaderHashList> HeaderHashList { get; } - public override MetaDataCache BlockHashIndex { get; } - public override MetaDataCache HeaderHashIndex { get; } - - public ClonedView(StoreView view) - { - this.Blocks = view.Blocks.CreateSnapshot(); - this.Transactions = view.Transactions.CreateSnapshot(); - this.Storages = view.Storages.CreateSnapshot(); - this.HeaderHashList = view.HeaderHashList.CreateSnapshot(); - this.BlockHashIndex = view.BlockHashIndex.CreateSnapshot(); - this.HeaderHashIndex = view.HeaderHashIndex.CreateSnapshot(); - } - } -} diff --git a/src/neo/IO/Caching/DataCache.cs b/src/neo/Persistence/DataCache.cs similarity index 82% rename from src/neo/IO/Caching/DataCache.cs rename to src/neo/Persistence/DataCache.cs index b81ad3d7f7..a5b96fc5fe 100644 --- a/src/neo/IO/Caching/DataCache.cs +++ b/src/neo/Persistence/DataCache.cs @@ -1,24 +1,24 @@ +using Neo.IO; +using Neo.SmartContract; using System; using System.Collections.Generic; using System.Linq; -namespace Neo.IO.Caching +namespace Neo.Persistence { - public abstract class DataCache - where TKey : IEquatable, ISerializable - where TValue : class, ICloneable, ISerializable, new() + public abstract class DataCache { public class Trackable { - public TKey Key; - public TValue Item; + public StorageKey Key; + public StorageItem Item; public TrackState State; } - private readonly Dictionary dictionary = new Dictionary(); - private readonly HashSet changeSet = new HashSet(); + private readonly Dictionary dictionary = new Dictionary(); + private readonly HashSet changeSet = new HashSet(); - public TValue this[TKey key] + public StorageItem this[StorageKey key] { get { @@ -54,7 +54,7 @@ public TValue this[TKey key] /// /// Corresponding value to be added, in the case of sucess. /// If cached on dictionary, with any state rather than `Deleted`, an Exception will be raised. - public void Add(TKey key, TValue value) + public void Add(StorageKey key, StorageItem value) { lock (dictionary) { @@ -70,14 +70,14 @@ public void Add(TKey key, TValue value) } } - protected abstract void AddInternal(TKey key, TValue value); + protected abstract void AddInternal(StorageKey key, StorageItem value); /// /// Update internals with all changes cached on Dictionary which are not None. /// - public void Commit() + public virtual void Commit() { - LinkedList deletedItem = new LinkedList(); + LinkedList deletedItem = new LinkedList(); foreach (Trackable trackable in GetChangeSet()) switch (trackable.State) { @@ -94,23 +94,23 @@ public void Commit() deletedItem.AddFirst(trackable.Key); break; } - foreach (TKey key in deletedItem) + foreach (StorageKey key in deletedItem) { dictionary.Remove(key); } changeSet.Clear(); } - public DataCache CreateSnapshot() + public DataCache CreateSnapshot() { - return new CloneCache(this); + return new ClonedCache(this); } /// /// Delete key from cached Dictionary or search in Internal. /// /// Key to be deleted. - public void Delete(TKey key) + public void Delete(StorageKey key) { lock (dictionary) { @@ -129,7 +129,7 @@ public void Delete(TKey key) } else { - TValue item = TryGetInternal(key); + StorageItem item = TryGetInternal(key); if (item == null) return; dictionary.Add(key, new Trackable { @@ -142,14 +142,14 @@ public void Delete(TKey key) } } - protected abstract void DeleteInternal(TKey key); + protected abstract void DeleteInternal(StorageKey key); /// /// Find the entries that start with the `key_prefix` /// - /// Must maintain the deserialized format of TKey + /// Must maintain the deserialized format of StorageKey /// Entries found with the desired prefix - public IEnumerable<(TKey Key, TValue Value)> Find(byte[] key_prefix = null) + public IEnumerable<(StorageKey Key, StorageItem Value)> Find(byte[] key_prefix = null) { foreach (var (key, value) in Seek(key_prefix, SeekDirection.Forward)) if (key.ToArray().AsSpan().StartsWith(key_prefix)) @@ -165,7 +165,7 @@ public void Delete(TKey key) /// End key (exclusive) /// The search direction. /// Entries found with the desired range - public IEnumerable<(TKey Key, TValue Value)> FindRange(byte[] start, byte[] end, SeekDirection direction = SeekDirection.Forward) + public IEnumerable<(StorageKey Key, StorageItem Value)> FindRange(byte[] start, byte[] end, SeekDirection direction = SeekDirection.Forward) { ByteArrayComparer comparer = direction == SeekDirection.Forward ? ByteArrayComparer.Default @@ -181,12 +181,12 @@ public IEnumerable GetChangeSet() { lock (dictionary) { - foreach (TKey key in changeSet) + foreach (StorageKey key in changeSet) yield return dictionary[key]; } } - public bool Contains(TKey key) + public bool Contains(StorageKey key) { lock (dictionary) { @@ -199,9 +199,9 @@ public bool Contains(TKey key) } } - protected abstract bool ContainsInternal(TKey key); + protected abstract bool ContainsInternal(StorageKey key); - protected abstract TValue GetInternal(TKey key); + protected abstract StorageItem GetInternal(StorageKey key); /// /// Try to Get a specific key from current cached dictionary. @@ -211,7 +211,7 @@ public bool Contains(TKey key) /// Function that may replace current object stored. /// If object already exists the factory passed as parameter will not be used. /// - public TValue GetAndChange(TKey key, Func factory = null) + public StorageItem GetAndChange(StorageKey key, Func factory = null) { lock (dictionary) { @@ -253,7 +253,7 @@ public TValue GetAndChange(TKey key, Func factory = null) } } - public TValue GetOrAdd(TKey key, Func factory) + public StorageItem GetOrAdd(StorageKey key, Func factory) { lock (dictionary) { @@ -294,10 +294,10 @@ public TValue GetOrAdd(TKey key, Func factory) /// The key to be sought /// The direction of seek /// An enumerator containing all the entries after seeking. - public IEnumerable<(TKey Key, TValue Value)> Seek(byte[] keyOrPrefix = null, SeekDirection direction = SeekDirection.Forward) + public IEnumerable<(StorageKey Key, StorageItem Value)> Seek(byte[] keyOrPrefix = null, SeekDirection direction = SeekDirection.Forward) { - IEnumerable<(byte[], TKey, TValue)> cached; - HashSet cachedKeySet; + IEnumerable<(byte[], StorageKey, StorageItem)> cached; + HashSet cachedKeySet; ByteArrayComparer comparer = direction == SeekDirection.Forward ? ByteArrayComparer.Default : ByteArrayComparer.Reverse; lock (dictionary) { @@ -311,7 +311,7 @@ public TValue GetOrAdd(TKey key, Func factory) )) .OrderBy(p => p.KeyBytes, comparer) .ToArray(); - cachedKeySet = new HashSet(dictionary.Keys); + cachedKeySet = new HashSet(dictionary.Keys); } var uncached = SeekInternal(keyOrPrefix ?? Array.Empty(), direction) .Where(p => !cachedKeySet.Contains(p.Key)) @@ -324,7 +324,7 @@ public TValue GetOrAdd(TKey key, Func factory) using (var e1 = cached.GetEnumerator()) using (var e2 = uncached.GetEnumerator()) { - (byte[] KeyBytes, TKey Key, TValue Item) i1, i2; + (byte[] KeyBytes, StorageKey Key, StorageItem Item) i1, i2; bool c1 = e1.MoveNext(); bool c2 = e2.MoveNext(); i1 = c1 ? e1.Current : default; @@ -347,9 +347,9 @@ public TValue GetOrAdd(TKey key, Func factory) } } - protected abstract IEnumerable<(TKey Key, TValue Value)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction); + protected abstract IEnumerable<(StorageKey Key, StorageItem Value)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction); - public TValue TryGet(TKey key) + public StorageItem TryGet(StorageKey key) { lock (dictionary) { @@ -358,7 +358,7 @@ public TValue TryGet(TKey key) if (trackable.State == TrackState.Deleted) return null; return trackable.Item; } - TValue value = TryGetInternal(key); + StorageItem value = TryGetInternal(key); if (value == null) return null; dictionary.Add(key, new Trackable { @@ -370,8 +370,8 @@ public TValue TryGet(TKey key) } } - protected abstract TValue TryGetInternal(TKey key); + protected abstract StorageItem TryGetInternal(StorageKey key); - protected abstract void UpdateInternal(TKey key, TValue value); + protected abstract void UpdateInternal(StorageKey key, StorageItem value); } } diff --git a/src/neo/Persistence/IReadOnlyStore.cs b/src/neo/Persistence/IReadOnlyStore.cs index 86b7ef4877..f13fd56d7f 100644 --- a/src/neo/Persistence/IReadOnlyStore.cs +++ b/src/neo/Persistence/IReadOnlyStore.cs @@ -8,8 +8,8 @@ namespace Neo.Persistence /// public interface IReadOnlyStore { - IEnumerable<(byte[] Key, byte[] Value)> Seek(byte table, byte[] key, SeekDirection direction); - byte[] TryGet(byte table, byte[] key); - bool Contains(byte table, byte[] key); + IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] key, SeekDirection direction); + byte[] TryGet(byte[] key); + bool Contains(byte[] key); } } diff --git a/src/neo/Persistence/ISnapshot.cs b/src/neo/Persistence/ISnapshot.cs index 335089d4bc..12bb0780af 100644 --- a/src/neo/Persistence/ISnapshot.cs +++ b/src/neo/Persistence/ISnapshot.cs @@ -8,7 +8,7 @@ namespace Neo.Persistence public interface ISnapshot : IDisposable, IReadOnlyStore { void Commit(); - void Delete(byte table, byte[] key); - void Put(byte table, byte[] key, byte[] value); + void Delete(byte[] key); + void Put(byte[] key, byte[] value); } } diff --git a/src/neo/Persistence/IStore.cs b/src/neo/Persistence/IStore.cs index 197cecdca3..dbf2087760 100644 --- a/src/neo/Persistence/IStore.cs +++ b/src/neo/Persistence/IStore.cs @@ -7,12 +7,9 @@ namespace Neo.Persistence ///
public interface IStore : IDisposable, IReadOnlyStore { - void Delete(byte table, byte[] key); + void Delete(byte[] key); ISnapshot GetSnapshot(); - void Put(byte table, byte[] key, byte[] value); - void PutSync(byte table, byte[] key, byte[] value) - { - Put(table, key, value); - } + void Put(byte[] key, byte[] value); + void PutSync(byte[] key, byte[] value) => Put(key, value); } } diff --git a/src/neo/Persistence/MemorySnapshot.cs b/src/neo/Persistence/MemorySnapshot.cs index e5800c08ae..7b3c77dbe8 100644 --- a/src/neo/Persistence/MemorySnapshot.cs +++ b/src/neo/Persistence/MemorySnapshot.cs @@ -9,62 +9,59 @@ namespace Neo.Persistence { internal class MemorySnapshot : ISnapshot { - private readonly ConcurrentDictionary[] innerData; - private readonly ImmutableDictionary[] immutableData; - private readonly ConcurrentDictionary[] writeBatch; + private readonly ConcurrentDictionary innerData; + private readonly ImmutableDictionary immutableData; + private readonly ConcurrentDictionary writeBatch; - public MemorySnapshot(ConcurrentDictionary[] innerData) + public MemorySnapshot(ConcurrentDictionary innerData) { this.innerData = innerData; - this.immutableData = innerData.Select(p => p.ToImmutableDictionary(ByteArrayEqualityComparer.Default)).ToArray(); - this.writeBatch = new ConcurrentDictionary[innerData.Length]; - for (int i = 0; i < writeBatch.Length; i++) - writeBatch[i] = new ConcurrentDictionary(ByteArrayEqualityComparer.Default); + this.immutableData = innerData.ToImmutableDictionary(ByteArrayEqualityComparer.Default); + this.writeBatch = new ConcurrentDictionary(ByteArrayEqualityComparer.Default); } public void Commit() { - for (int i = 0; i < writeBatch.Length; i++) - foreach (var pair in writeBatch[i]) - if (pair.Value is null) - innerData[i].TryRemove(pair.Key, out _); - else - innerData[i][pair.Key] = pair.Value; + foreach (var pair in writeBatch) + if (pair.Value is null) + innerData.TryRemove(pair.Key, out _); + else + innerData[pair.Key] = pair.Value; } - public void Delete(byte table, byte[] key) + public void Delete(byte[] key) { - writeBatch[table][key.EnsureNotNull()] = null; + writeBatch[key.EnsureNotNull()] = null; } public void Dispose() { } - public void Put(byte table, byte[] key, byte[] value) + public void Put(byte[] key, byte[] value) { - writeBatch[table][key.EnsureNotNull()] = value; + writeBatch[key.EnsureNotNull()] = value; } - public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte table, byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) + public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) { ByteArrayComparer comparer = direction == SeekDirection.Forward ? ByteArrayComparer.Default : ByteArrayComparer.Reverse; - IEnumerable> records = immutableData[table]; + IEnumerable> records = immutableData; if (keyOrPrefix?.Length > 0) records = records.Where(p => comparer.Compare(p.Key, keyOrPrefix) >= 0); records = records.OrderBy(p => p.Key, comparer); return records.Select(p => (p.Key, p.Value)); } - public byte[] TryGet(byte table, byte[] key) + public byte[] TryGet(byte[] key) { - immutableData[table].TryGetValue(key.EnsureNotNull(), out byte[] value); + immutableData.TryGetValue(key.EnsureNotNull(), out byte[] value); return value; } - public bool Contains(byte table, byte[] key) + public bool Contains(byte[] key) { - return innerData[table].ContainsKey(key.EnsureNotNull()); + return innerData.ContainsKey(key.EnsureNotNull()); } } } diff --git a/src/neo/Persistence/MemoryStore.cs b/src/neo/Persistence/MemoryStore.cs index fdcd06f013..705f509b87 100644 --- a/src/neo/Persistence/MemoryStore.cs +++ b/src/neo/Persistence/MemoryStore.cs @@ -8,18 +8,11 @@ namespace Neo.Persistence { public class MemoryStore : IStore { - private readonly ConcurrentDictionary[] innerData; + private readonly ConcurrentDictionary innerData = new ConcurrentDictionary(ByteArrayEqualityComparer.Default); - public MemoryStore() + public void Delete(byte[] key) { - innerData = new ConcurrentDictionary[256]; - for (int i = 0; i < innerData.Length; i++) - innerData[i] = new ConcurrentDictionary(ByteArrayEqualityComparer.Default); - } - - public void Delete(byte table, byte[] key) - { - innerData[table].TryRemove(key.EnsureNotNull(), out _); + innerData.TryRemove(key.EnsureNotNull(), out _); } public void Dispose() @@ -31,15 +24,15 @@ public ISnapshot GetSnapshot() return new MemorySnapshot(innerData); } - public void Put(byte table, byte[] key, byte[] value) + public void Put(byte[] key, byte[] value) { - innerData[table][key.EnsureNotNull()] = value; + innerData[key.EnsureNotNull()] = value; } - public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte table, byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) + public IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) { ByteArrayComparer comparer = direction == SeekDirection.Forward ? ByteArrayComparer.Default : ByteArrayComparer.Reverse; - IEnumerable> records = innerData[table]; + IEnumerable> records = innerData; if (keyOrPrefix?.Length > 0) records = records.Where(p => comparer.Compare(p.Key, keyOrPrefix) >= 0); records = records.OrderBy(p => p.Key, comparer); @@ -47,15 +40,15 @@ public void Put(byte table, byte[] key, byte[] value) yield return (pair.Key, pair.Value); } - public byte[] TryGet(byte table, byte[] key) + public byte[] TryGet(byte[] key) { - innerData[table].TryGetValue(key.EnsureNotNull(), out byte[] value); + innerData.TryGetValue(key.EnsureNotNull(), out byte[] value); return value; } - public bool Contains(byte table, byte[] key) + public bool Contains(byte[] key) { - return innerData[table].ContainsKey(key.EnsureNotNull()); + return innerData.ContainsKey(key.EnsureNotNull()); } } } diff --git a/src/neo/Persistence/Prefixes.cs b/src/neo/Persistence/Prefixes.cs deleted file mode 100644 index 3b63665326..0000000000 --- a/src/neo/Persistence/Prefixes.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Neo.Persistence -{ - internal static class Prefixes - { - public const byte DATA_Block = 0x01; - public const byte DATA_Transaction = 0x02; - - public const byte ST_Storage = 0x70; - - public const byte IX_HeaderHashList = 0x80; - public const byte IX_CurrentBlock = 0xc0; - public const byte IX_CurrentHeader = 0xc1; - } -} diff --git a/src/neo/Persistence/ReadOnlyView.cs b/src/neo/Persistence/ReadOnlyView.cs deleted file mode 100644 index a874c33697..0000000000 --- a/src/neo/Persistence/ReadOnlyView.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using Neo.Ledger; -using System; - -namespace Neo.Persistence -{ - /// - /// Provide a read-only for accessing directly from database instead of from snapshot. - /// - public class ReadOnlyView : StoreView - { - private readonly IReadOnlyStore store; - - public override DataCache Blocks => new StoreDataCache(store, Prefixes.DATA_Block); - public override DataCache Transactions => new StoreDataCache(store, Prefixes.DATA_Transaction); - public override DataCache Storages => new StoreDataCache(store, Prefixes.ST_Storage); - public override DataCache, HeaderHashList> HeaderHashList => new StoreDataCache, HeaderHashList>(store, Prefixes.IX_HeaderHashList); - public override MetaDataCache BlockHashIndex => new StoreMetaDataCache(store, Prefixes.IX_CurrentBlock); - public override MetaDataCache HeaderHashIndex => new StoreMetaDataCache(store, Prefixes.IX_CurrentHeader); - - public ReadOnlyView(IReadOnlyStore store) - { - this.store = store; - } - - public override void Commit() - { - throw new NotSupportedException(); - } - } -} diff --git a/src/neo/IO/Caching/SeekDirection.cs b/src/neo/Persistence/SeekDirection.cs similarity index 78% rename from src/neo/IO/Caching/SeekDirection.cs rename to src/neo/Persistence/SeekDirection.cs index 5387fd8311..ca48561ab3 100644 --- a/src/neo/IO/Caching/SeekDirection.cs +++ b/src/neo/Persistence/SeekDirection.cs @@ -1,4 +1,4 @@ -namespace Neo.IO.Caching +namespace Neo.Persistence { public enum SeekDirection : sbyte { diff --git a/src/neo/Persistence/SnapshotCache.cs b/src/neo/Persistence/SnapshotCache.cs new file mode 100644 index 0000000000..a6e6bf9452 --- /dev/null +++ b/src/neo/Persistence/SnapshotCache.cs @@ -0,0 +1,66 @@ +using Neo.IO; +using Neo.SmartContract; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.Persistence +{ + public class SnapshotCache : DataCache, IDisposable + { + private readonly IReadOnlyStore store; + private readonly ISnapshot snapshot; + + public SnapshotCache(IReadOnlyStore store) + { + this.store = store; + this.snapshot = store as ISnapshot; + } + + protected override void AddInternal(StorageKey key, StorageItem value) + { + snapshot?.Put(key.ToArray(), value.ToArray()); + } + + protected override void DeleteInternal(StorageKey key) + { + snapshot?.Delete(key.ToArray()); + } + + public override void Commit() + { + base.Commit(); + snapshot.Commit(); + } + + protected override bool ContainsInternal(StorageKey key) + { + return store.Contains(key.ToArray()); + } + + public void Dispose() + { + snapshot?.Dispose(); + } + + protected override StorageItem GetInternal(StorageKey key) + { + return store.TryGet(key.ToArray()).AsSerializable(); + } + + protected override IEnumerable<(StorageKey, StorageItem)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction) + { + return store.Seek(keyOrPrefix, direction).Select(p => (p.Key.AsSerializable(), p.Value.AsSerializable())); + } + + protected override StorageItem TryGetInternal(StorageKey key) + { + return store.TryGet(key.ToArray())?.AsSerializable(); + } + + protected override void UpdateInternal(StorageKey key, StorageItem value) + { + snapshot?.Put(key.ToArray(), value.ToArray()); + } + } +} diff --git a/src/neo/Persistence/SnapshotView.cs b/src/neo/Persistence/SnapshotView.cs deleted file mode 100644 index ce66270509..0000000000 --- a/src/neo/Persistence/SnapshotView.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using Neo.Ledger; -using System; - -namespace Neo.Persistence -{ - /// - /// Provide a for accessing snapshots. - /// - public class SnapshotView : StoreView, IDisposable - { - private readonly ISnapshot snapshot; - - public override DataCache Blocks { get; } - public override DataCache Transactions { get; } - public override DataCache Storages { get; } - public override DataCache, HeaderHashList> HeaderHashList { get; } - public override MetaDataCache BlockHashIndex { get; } - public override MetaDataCache HeaderHashIndex { get; } - - public SnapshotView(IStore store) - { - this.snapshot = store.GetSnapshot(); - Blocks = new StoreDataCache(snapshot, Prefixes.DATA_Block); - Transactions = new StoreDataCache(snapshot, Prefixes.DATA_Transaction); - Storages = new StoreDataCache(snapshot, Prefixes.ST_Storage); - HeaderHashList = new StoreDataCache, HeaderHashList>(snapshot, Prefixes.IX_HeaderHashList); - BlockHashIndex = new StoreMetaDataCache(snapshot, Prefixes.IX_CurrentBlock); - HeaderHashIndex = new StoreMetaDataCache(snapshot, Prefixes.IX_CurrentHeader); - } - - public override void Commit() - { - base.Commit(); - snapshot.Commit(); - } - - public void Dispose() - { - snapshot.Dispose(); - } - } -} diff --git a/src/neo/Persistence/StoreDataCache.cs b/src/neo/Persistence/StoreDataCache.cs deleted file mode 100644 index 570114198d..0000000000 --- a/src/neo/Persistence/StoreDataCache.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Neo.Persistence -{ - public class StoreDataCache : DataCache - where TKey : IEquatable, ISerializable, new() - where TValue : class, ICloneable, ISerializable, new() - { - private readonly IReadOnlyStore store; - private readonly ISnapshot snapshot; - private readonly byte prefix; - - public StoreDataCache(IReadOnlyStore store, byte prefix) - { - this.store = store; - this.snapshot = store as ISnapshot; - this.prefix = prefix; - } - - protected override void AddInternal(TKey key, TValue value) - { - snapshot?.Put(prefix, key.ToArray(), value.ToArray()); - } - - protected override void DeleteInternal(TKey key) - { - snapshot?.Delete(prefix, key.ToArray()); - } - - protected override bool ContainsInternal(TKey key) - { - return store.Contains(prefix, key.ToArray()); - } - - protected override TValue GetInternal(TKey key) - { - return store.TryGet(prefix, key.ToArray()).AsSerializable(); - } - - protected override IEnumerable<(TKey, TValue)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction) - { - return store.Seek(prefix, keyOrPrefix, direction).Select(p => (p.Key.AsSerializable(), p.Value.AsSerializable())); - } - - protected override TValue TryGetInternal(TKey key) - { - return store.TryGet(prefix, key.ToArray())?.AsSerializable(); - } - - protected override void UpdateInternal(TKey key, TValue value) - { - snapshot?.Put(prefix, key.ToArray(), value.ToArray()); - } - } -} diff --git a/src/neo/Persistence/StoreMetaDataCache.cs b/src/neo/Persistence/StoreMetaDataCache.cs deleted file mode 100644 index 043eb20a07..0000000000 --- a/src/neo/Persistence/StoreMetaDataCache.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using System; - -namespace Neo.Persistence -{ - public class StoreMetaDataCache : MetaDataCache - where T : class, ICloneable, ISerializable, new() - { - private readonly IReadOnlyStore store; - private readonly ISnapshot snapshot; - private readonly byte prefix; - - public StoreMetaDataCache(IReadOnlyStore store, byte prefix, Func factory = null) - : base(factory) - { - this.store = store; - this.snapshot = store as ISnapshot; - this.prefix = prefix; - } - - protected override void AddInternal(T item) - { - snapshot?.Put(prefix, null, item.ToArray()); - } - - protected override T TryGetInternal() - { - return store.TryGet(prefix, null)?.AsSerializable(); - } - - protected override void UpdateInternal(T item) - { - snapshot?.Put(prefix, null, item.ToArray()); - } - } -} diff --git a/src/neo/Persistence/StoreView.cs b/src/neo/Persistence/StoreView.cs deleted file mode 100644 index a31d27ec57..0000000000 --- a/src/neo/Persistence/StoreView.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; - -namespace Neo.Persistence -{ - /// - /// It provides a set of properties and methods for reading formatted data from the underlying storage. Such as and . - /// - public abstract class StoreView - { - public abstract DataCache Blocks { get; } - public abstract DataCache Transactions { get; } - public abstract DataCache Storages { get; } - public abstract DataCache, HeaderHashList> HeaderHashList { get; } - public abstract MetaDataCache BlockHashIndex { get; } - public abstract MetaDataCache HeaderHashIndex { get; } - - public uint Height => BlockHashIndex.Get().Index; - public uint HeaderHeight => HeaderHashIndex.Get().Index; - public UInt256 CurrentBlockHash => BlockHashIndex.Get().Hash; - public UInt256 CurrentHeaderHash => HeaderHashIndex.Get().Hash; - - public StoreView Clone() - { - return new ClonedView(this); - } - - public virtual void Commit() - { - Blocks.Commit(); - Transactions.Commit(); - Storages.Commit(); - HeaderHashList.Commit(); - BlockHashIndex.Commit(); - HeaderHashIndex.Commit(); - } - - public bool ContainsBlock(UInt256 hash) - { - TrimmedBlock state = Blocks.TryGet(hash); - if (state == null) return false; - return state.IsBlock; - } - - public bool ContainsTransaction(UInt256 hash) - { - return Transactions.Contains(hash); - } - - public Block GetBlock(UInt256 hash) - { - TrimmedBlock state = Blocks.TryGet(hash); - if (state == null) return null; - if (!state.IsBlock) return null; - return state.GetBlock(Transactions); - } - - public Header GetHeader(UInt256 hash) - { - return Blocks.TryGet(hash)?.Header; - } - - public Transaction GetTransaction(UInt256 hash) - { - return Transactions.TryGet(hash)?.Transaction; - } - } -} diff --git a/src/neo/IO/Caching/TrackState.cs b/src/neo/Persistence/TrackState.cs similarity index 81% rename from src/neo/IO/Caching/TrackState.cs rename to src/neo/Persistence/TrackState.cs index cba7daec0c..30bc210539 100644 --- a/src/neo/IO/Caching/TrackState.cs +++ b/src/neo/Persistence/TrackState.cs @@ -1,4 +1,4 @@ -namespace Neo.IO.Caching +namespace Neo.Persistence { public enum TrackState : byte { diff --git a/src/neo/Plugins/IApplicationEngineProvider.cs b/src/neo/Plugins/IApplicationEngineProvider.cs index df3427219c..43f50daa3a 100644 --- a/src/neo/Plugins/IApplicationEngineProvider.cs +++ b/src/neo/Plugins/IApplicationEngineProvider.cs @@ -6,6 +6,6 @@ namespace Neo.Plugins { public interface IApplicationEngineProvider { - ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock, long gas); + ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, long gas); } } diff --git a/src/neo/Plugins/IPersistencePlugin.cs b/src/neo/Plugins/IPersistencePlugin.cs index 64be4b8b0b..76bb126cc5 100644 --- a/src/neo/Plugins/IPersistencePlugin.cs +++ b/src/neo/Plugins/IPersistencePlugin.cs @@ -8,8 +8,8 @@ namespace Neo.Plugins { public interface IPersistencePlugin { - void OnPersist(Block block, StoreView snapshot, IReadOnlyList applicationExecutedList) { } - void OnCommit(Block block, StoreView snapshot) { } + void OnPersist(Block block, DataCache snapshot, IReadOnlyList applicationExecutedList) { } + void OnCommit(Block block, DataCache snapshot) { } bool ShouldThrowExceptionFromCommit(Exception ex) => false; } } diff --git a/src/neo/SmartContract/ApplicationEngine.Blockchain.cs b/src/neo/SmartContract/ApplicationEngine.Blockchain.cs deleted file mode 100644 index fef191fb44..0000000000 --- a/src/neo/SmartContract/ApplicationEngine.Blockchain.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.Persistence; -using System; -using System.Numerics; - -namespace Neo.SmartContract -{ - partial class ApplicationEngine - { - public static readonly InteropDescriptor System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", nameof(GetBlockchainHeight), 1 << 4, CallFlags.ReadStates); - public static readonly InteropDescriptor System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", nameof(GetBlock), 1 << 16, CallFlags.ReadStates); - public static readonly InteropDescriptor System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", nameof(GetTransaction), 1 << 15, CallFlags.ReadStates); - public static readonly InteropDescriptor System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", nameof(GetTransactionHeight), 1 << 15, CallFlags.ReadStates); - public static readonly InteropDescriptor System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", nameof(GetTransactionFromBlock), 1 << 15, CallFlags.ReadStates); - - protected internal uint GetBlockchainHeight() - { - return Snapshot.Height; - } - - protected internal Block GetBlock(byte[] indexOrHash) - { - UInt256 hash; - if (indexOrHash.Length < UInt256.Length) - { - BigInteger bi = new BigInteger(indexOrHash); - if (bi < uint.MinValue || bi > uint.MaxValue) - throw new ArgumentOutOfRangeException(nameof(indexOrHash)); - hash = Blockchain.Singleton.GetBlockHash((uint)bi); - } - else if (indexOrHash.Length == UInt256.Length) - { - hash = new UInt256(indexOrHash); - } - else - { - throw new ArgumentException(); - } - if (hash is null) return null; - Block block = Snapshot.GetBlock(hash); - if (block is null) return null; - if (!IsTraceableBlock(Snapshot, block.Index)) return null; - return block; - } - - protected internal Transaction GetTransaction(UInt256 hash) - { - TransactionState state = Snapshot.Transactions.TryGet(hash); - if (state != null && !IsTraceableBlock(Snapshot, state.BlockIndex)) state = null; - return state?.Transaction; - } - - protected internal int GetTransactionHeight(UInt256 hash) - { - TransactionState state = Snapshot.Transactions.TryGet(hash); - if (state is null) return -1; - if (!IsTraceableBlock(Snapshot, state.BlockIndex)) return -1; - return (int)state.BlockIndex; - } - - protected internal Transaction GetTransactionFromBlock(byte[] blockIndexOrHash, int txIndex) - { - UInt256 hash; - if (blockIndexOrHash.Length < UInt256.Length) - { - BigInteger bi = new BigInteger(blockIndexOrHash); - if (bi < uint.MinValue || bi > uint.MaxValue) - throw new ArgumentOutOfRangeException(nameof(blockIndexOrHash)); - hash = Blockchain.Singleton.GetBlockHash((uint)bi); - } - else if (blockIndexOrHash.Length == UInt256.Length) - { - hash = new UInt256(blockIndexOrHash); - } - else - { - throw new ArgumentException(); - } - if (hash is null) return null; - TrimmedBlock block = Snapshot.Blocks.TryGet(hash); - if (block is null) return null; - if (!IsTraceableBlock(Snapshot, block.Index)) return null; - if (txIndex < 0 || txIndex >= block.Hashes.Length - 1) - throw new ArgumentOutOfRangeException(nameof(txIndex)); - return Snapshot.GetTransaction(block.Hashes[txIndex + 1]); - } - - private static bool IsTraceableBlock(StoreView snapshot, uint index) - { - if (index > snapshot.Height) return false; - return index + ProtocolSettings.Default.MaxTraceableBlocks > snapshot.Height; - } - } -} diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index e08a17099b..c52527bb6b 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -41,7 +41,7 @@ protected internal void CallContract(UInt160 contractHash, string method, CallFl protected internal void CallNativeContract(int id) { NativeContract contract = NativeContract.GetContract(id); - if (contract is null || contract.ActiveBlockIndex > Snapshot.Height) + if (contract is null || contract.ActiveBlockIndex > NativeContract.Ledger.CurrentIndex(Snapshot)) throw new InvalidOperationException(); contract.Invoke(this); } diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index 8d74182e68..5a1edd1598 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -70,7 +70,7 @@ protected internal bool CheckWitnessInternal(UInt160 hash) else { OracleRequest request = NativeContract.Oracle.GetRequest(Snapshot, response.Id); - signers = Snapshot.GetTransaction(request.OriginalTxid).Signers; + signers = NativeContract.Ledger.GetTransaction(Snapshot, request.OriginalTxid).Signers; } Signer signer = signers.FirstOrDefault(p => p.Account.Equals(hash)); if (signer is null) return false; diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index 24a7f5d2eb..17b62fba63 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -1,4 +1,3 @@ -using Neo.Ledger; using Neo.SmartContract.Iterators; using Neo.SmartContract.Native; using System; @@ -53,7 +52,7 @@ protected internal StorageContext AsReadOnly(StorageContext context) protected internal byte[] Get(StorageContext context, byte[] key) { - return Snapshot.Storages.TryGet(new StorageKey + return Snapshot.TryGet(new StorageKey { Id = context.Id, Key = key.ToArray() @@ -73,7 +72,7 @@ protected internal IIterator Find(StorageContext context, byte[] prefix, FindOpt if ((options.HasFlag(FindOptions.PickField0) || options.HasFlag(FindOptions.PickField1)) && !options.HasFlag(FindOptions.DeserializeValues)) throw new ArgumentException(); byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); - return new StorageIterator(Snapshot.Storages.Find(prefix_key).GetEnumerator(), options, ReferenceCounter); + return new StorageIterator(Snapshot.Find(prefix_key).GetEnumerator(), options, ReferenceCounter); } protected internal void Put(StorageContext context, byte[] key, byte[] value) @@ -97,11 +96,11 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto Id = context.Id, Key = key }; - StorageItem item = Snapshot.Storages.GetAndChange(skey); + StorageItem item = Snapshot.GetAndChange(skey); if (item is null) { newDataSize = key.Length + value.Length; - Snapshot.Storages.Add(skey, item = new StorageItem()); + Snapshot.Add(skey, item = new StorageItem()); } else { @@ -128,9 +127,9 @@ protected internal void Delete(StorageContext context, byte[] key) Id = context.Id, Key = key }; - if (Snapshot.Storages.TryGet(skey)?.IsConstant == true) + if (Snapshot.TryGet(skey)?.IsConstant == true) throw new InvalidOperationException(); - Snapshot.Storages.Delete(skey); + Snapshot.Delete(skey); } } } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 117ddfb059..936eaac7f6 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -42,7 +42,7 @@ public partial class ApplicationEngine : ExecutionEngine private List Disposables => disposables ??= new List(); public TriggerType Trigger { get; } public IVerifiable ScriptContainer { get; } - public StoreView Snapshot { get; } + public DataCache Snapshot { get; } public Block PersistingBlock { get; } public long GasConsumed { get; private set; } = 0; public long GasLeft => gas_amount - GasConsumed; @@ -52,7 +52,7 @@ public partial class ApplicationEngine : ExecutionEngine public UInt160 EntryScriptHash => EntryContext?.GetScriptHash(); public IReadOnlyList Notifications => notifications ?? (IReadOnlyList)Array.Empty(); - protected ApplicationEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock, long gas) + protected ApplicationEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, long gas) { this.Trigger = trigger; this.ScriptContainer = container; @@ -146,7 +146,7 @@ internal T CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, st return (T)Convert(Pop(), new InteropParameterDescriptor(typeof(T))); } - public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock = null, long gas = TestModeGas) + public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, long gas = TestModeGas) { return applicationEngineProvider?.Create(trigger, container, snapshot, persistingBlock, gas) ?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, gas); @@ -318,16 +318,17 @@ internal void StepOut() throw new InvalidOperationException("StepOut failed.", FaultException); } - private static Block CreateDummyBlock(StoreView snapshot) + private static Block CreateDummyBlock(DataCache snapshot) { - var currentBlock = snapshot.Blocks[snapshot.CurrentBlockHash]; + UInt256 hash = NativeContract.Ledger.CurrentHash(snapshot); + var currentBlock = NativeContract.Ledger.GetBlock(snapshot, hash); return new Block { Version = 0, - PrevHash = snapshot.CurrentBlockHash, + PrevHash = hash, MerkleRoot = new UInt256(), Timestamp = currentBlock.Timestamp + Blockchain.MillisecondsPerBlock, - Index = snapshot.Height + 1, + Index = currentBlock.Index + 1, NextConsensus = currentBlock.NextConsensus, Witness = new Witness { @@ -354,9 +355,9 @@ internal static void ResetApplicationEngineProvider() Exchange(ref applicationEngineProvider, null); } - public static ApplicationEngine Run(byte[] script, StoreView snapshot = null, IVerifiable container = null, Block persistingBlock = null, int offset = 0, long gas = TestModeGas) + public static ApplicationEngine Run(byte[] script, DataCache snapshot = null, IVerifiable container = null, Block persistingBlock = null, int offset = 0, long gas = TestModeGas) { - SnapshotView disposable = null; + SnapshotCache disposable = null; if (snapshot is null) { disposable = Blockchain.Singleton.GetSnapshot(); diff --git a/src/neo/SmartContract/Contract.cs b/src/neo/SmartContract/Contract.cs index ca5a27b945..13246faf8b 100644 --- a/src/neo/SmartContract/Contract.cs +++ b/src/neo/SmartContract/Contract.cs @@ -106,5 +106,10 @@ public static byte[] CreateSignatureRedeemScript(ECPoint publicKey) return sb.ToArray(); } } + + public static UInt160 GetBFTAddress(ECPoint[] pubkeys) + { + return CreateMultiSigRedeemScript(pubkeys.Length - (pubkeys.Length - 1) / 3, pubkeys).ToScriptHash(); + } } } diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index 0b2a3a5447..40d44b424e 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -93,7 +93,7 @@ public IReadOnlyList ScriptHashes return _ScriptHashes; } - using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) { _ScriptHashes = Verifiable.GetScriptHashesForVerifying(snapshot); } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index e10c076829..5f0e076c4e 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -153,7 +153,7 @@ public static UInt160 ToScriptHash(this ReadOnlySpan script) return new UInt160(Crypto.Hash160(script)); } - public static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snapshot, long gas) + public static bool VerifyWitnesses(this IVerifiable verifiable, DataCache snapshot, long gas) { if (gas < 0) return false; if (gas > MaxVerificationGas) gas = MaxVerificationGas; @@ -177,10 +177,10 @@ public static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snapsh return true; } - internal static bool VerifyWitness(this IVerifiable verifiable, StoreView snapshot, UInt160 hash, Witness witness, long gas, out long fee) + internal static bool VerifyWitness(this IVerifiable verifiable, DataCache snapshot, UInt160 hash, Witness witness, long gas, out long fee) { fee = 0; - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, verifiable, snapshot?.Clone(), null, gas)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, verifiable, snapshot?.CreateSnapshot(), null, gas)) { CallFlags callFlags = !witness.VerificationScript.IsStandardContract() ? CallFlags.ReadStates : CallFlags.None; byte[] verification = witness.VerificationScript; diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 4d6697c8b3..2bf9bd9231 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -1,7 +1,6 @@ #pragma warning disable IDE0051 using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; @@ -64,9 +63,9 @@ internal ContractManagement() Manifest.Abi.Events = events.ToArray(); } - private int GetNextAvailableId(StoreView snapshot) + private int GetNextAvailableId(DataCache snapshot) { - StorageItem item = snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_NextAvailableId), () => new StorageItem(1)); + StorageItem item = snapshot.GetAndChange(CreateStorageKey(Prefix_NextAvailableId), () => new StorageItem(1)); int value = (int)(BigInteger)item; item.Add(1); return value; @@ -74,7 +73,7 @@ private int GetNextAvailableId(StoreView snapshot) internal override void Initialize(ApplicationEngine engine) { - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MinimumDeploymentFee), new StorageItem(10_00000000)); + engine.Snapshot.Add(CreateStorageKey(Prefix_MinimumDeploymentFee), new StorageItem(10_00000000)); } internal override void OnPersist(ApplicationEngine engine) @@ -83,7 +82,7 @@ internal override void OnPersist(ApplicationEngine engine) { if (contract.ActiveBlockIndex != engine.PersistingBlock.Index) continue; - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState + engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState { Id = contract.Id, Nef = contract.Nef, @@ -95,9 +94,9 @@ internal override void OnPersist(ApplicationEngine engine) } [ContractMethod(0_01000000, CallFlags.ReadStates)] - private long GetMinimumDeploymentFee(StoreView snapshot) + private long GetMinimumDeploymentFee(DataCache snapshot) { - return (long)(BigInteger)snapshot.Storages[CreateStorageKey(Prefix_MinimumDeploymentFee)]; + return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_MinimumDeploymentFee)]; } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -105,19 +104,19 @@ private void SetMinimumDeploymentFee(ApplicationEngine engine, BigInteger value) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MinimumDeploymentFee)).Set(value); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MinimumDeploymentFee)).Set(value); } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public ContractState GetContract(StoreView snapshot, UInt160 hash) + public ContractState GetContract(DataCache snapshot, UInt160 hash) { - return snapshot.Storages.TryGet(CreateStorageKey(Prefix_Contract).Add(hash))?.GetInteroperable(); + return snapshot.TryGet(CreateStorageKey(Prefix_Contract).Add(hash))?.GetInteroperable(); } - public IEnumerable ListContracts(StoreView snapshot) + public IEnumerable ListContracts(DataCache snapshot) { byte[] listContractsPrefix = CreateStorageKey(Prefix_Contract).ToArray(); - return snapshot.Storages.Find(listContractsPrefix).Select(kvp => kvp.Value.GetInteroperable()); + return snapshot.Find(listContractsPrefix).Select(kvp => kvp.Value.GetInteroperable()); } [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] @@ -145,7 +144,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma ContractManifest parsedManifest = ContractManifest.Parse(manifest); UInt160 hash = Helper.GetContractHash(tx.Sender, nef.CheckSum, parsedManifest.Name); StorageKey key = CreateStorageKey(Prefix_Contract).Add(hash); - if (engine.Snapshot.Storages.Contains(key)) + if (engine.Snapshot.Contains(key)) throw new InvalidOperationException($"Contract Already Exists: {hash}"); ContractState contract = new ContractState { @@ -158,7 +157,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma if (!contract.Manifest.IsValid(hash)) throw new InvalidOperationException($"Invalid Manifest Hash: {hash}"); - engine.Snapshot.Storages.Add(key, new StorageItem(contract)); + engine.Snapshot.Add(key, new StorageItem(contract)); // Execute _deploy @@ -184,7 +183,7 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, S engine.AddGas(engine.StoragePrice * ((nefFile?.Length ?? 0) + (manifest?.Length ?? 0))); - var contract = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Contract).Add(engine.CallingScriptHash))?.GetInteroperable(); + var contract = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Contract).Add(engine.CallingScriptHash))?.GetInteroperable(); if (contract is null) throw new InvalidOperationException($"Updating Contract Does Not Exist: {engine.CallingScriptHash}"); if (nefFile != null) @@ -221,11 +220,11 @@ private void Destroy(ApplicationEngine engine) { UInt160 hash = engine.CallingScriptHash; StorageKey ckey = CreateStorageKey(Prefix_Contract).Add(hash); - ContractState contract = engine.Snapshot.Storages.TryGet(ckey)?.GetInteroperable(); + ContractState contract = engine.Snapshot.TryGet(ckey)?.GetInteroperable(); if (contract is null) return; - engine.Snapshot.Storages.Delete(ckey); - foreach (var (key, _) in engine.Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id))) - engine.Snapshot.Storages.Delete(key); + engine.Snapshot.Delete(ckey); + foreach (var (key, _) in engine.Snapshot.Find(BitConverter.GetBytes(contract.Id))) + engine.Snapshot.Delete(key); engine.SendNotification(Hash, "Destroy", new VM.Types.Array { hash.ToArray() }); } } diff --git a/src/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/neo/SmartContract/Native/ContractMethodMetadata.cs index 563040721e..ffe7b7f3e3 100644 --- a/src/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -28,7 +28,7 @@ public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribu if (parameterInfos.Length > 0) { NeedApplicationEngine = parameterInfos[0].ParameterType.IsAssignableFrom(typeof(ApplicationEngine)); - NeedSnapshot = parameterInfos[0].ParameterType.IsAssignableFrom(typeof(StoreView)); + NeedSnapshot = parameterInfos[0].ParameterType.IsAssignableFrom(typeof(DataCache)); } if (NeedApplicationEngine || NeedSnapshot) this.Parameters = parameterInfos.Skip(1).Select(p => new InteropParameterDescriptor(p)).ToArray(); diff --git a/src/neo/SmartContract/Native/FungibleToken.cs b/src/neo/SmartContract/Native/FungibleToken.cs index 3e000c61b7..fa0e9d446a 100644 --- a/src/neo/SmartContract/Native/FungibleToken.cs +++ b/src/neo/SmartContract/Native/FungibleToken.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM.Types; @@ -61,11 +60,11 @@ internal protected virtual void Mint(ApplicationEngine engine, UInt160 account, { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); if (amount.IsZero) return; - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Account).Add(account), () => new StorageItem(new TState())); + StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Account).Add(account), () => new StorageItem(new TState())); TState state = storage.GetInteroperable(); OnBalanceChanging(engine, account, state, amount); state.Balance += amount; - storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero)); + storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero)); storage.Add(amount); PostTransfer(engine, null, account, amount, StackItem.Null, callOnPayment); } @@ -75,31 +74,31 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); if (amount.IsZero) return; StorageKey key = CreateStorageKey(Prefix_Account).Add(account); - StorageItem storage = engine.Snapshot.Storages.GetAndChange(key); + StorageItem storage = engine.Snapshot.GetAndChange(key); TState state = storage.GetInteroperable(); if (state.Balance < amount) throw new InvalidOperationException(); OnBalanceChanging(engine, account, state, -amount); if (state.Balance == amount) - engine.Snapshot.Storages.Delete(key); + engine.Snapshot.Delete(key); else state.Balance -= amount; - storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply)); + storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply)); storage.Add(-amount); PostTransfer(engine, account, null, amount, StackItem.Null, false); } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public virtual BigInteger TotalSupply(StoreView snapshot) + public virtual BigInteger TotalSupply(DataCache snapshot) { - StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_TotalSupply)); + StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_TotalSupply)); if (storage is null) return BigInteger.Zero; return storage; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) + public virtual BigInteger BalanceOf(DataCache snapshot, UInt160 account) { - StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_Account).Add(account)); + StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(account)); if (storage is null) return BigInteger.Zero; return storage.GetInteroperable().Balance; } @@ -111,7 +110,7 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 if (!from.Equals(engine.CallingScriptHash) && !engine.CheckWitnessInternal(from)) return false; StorageKey key_from = CreateStorageKey(Prefix_Account).Add(from); - StorageItem storage_from = engine.Snapshot.Storages.GetAndChange(key_from); + StorageItem storage_from = engine.Snapshot.GetAndChange(key_from); if (amount.IsZero) { if (storage_from != null) @@ -133,11 +132,11 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 { OnBalanceChanging(engine, from, state_from, -amount); if (state_from.Balance == amount) - engine.Snapshot.Storages.Delete(key_from); + engine.Snapshot.Delete(key_from); else state_from.Balance -= amount; StorageKey key_to = CreateStorageKey(Prefix_Account).Add(to); - StorageItem storage_to = engine.Snapshot.Storages.GetAndChange(key_to, () => new StorageItem(new TState())); + StorageItem storage_to = engine.Snapshot.GetAndChange(key_to, () => new StorageItem(new TState())); TState state_to = storage_to.GetInteroperable(); OnBalanceChanging(engine, to, state_to, amount); state_to.Balance += amount; diff --git a/src/neo/SmartContract/Native/GasToken.cs b/src/neo/SmartContract/Native/GasToken.cs index 81e61b666c..a8d51122f7 100644 --- a/src/neo/SmartContract/Native/GasToken.cs +++ b/src/neo/SmartContract/Native/GasToken.cs @@ -15,7 +15,7 @@ internal GasToken() internal override void Initialize(ApplicationEngine engine) { - UInt160 account = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators); + UInt160 account = Contract.GetBFTAddress(Blockchain.StandbyValidators); Mint(engine, account, 30_000_000 * Factor, false); } diff --git a/src/neo/SmartContract/Native/HashIndexState.cs b/src/neo/SmartContract/Native/HashIndexState.cs new file mode 100644 index 0000000000..c2bcb9831c --- /dev/null +++ b/src/neo/SmartContract/Native/HashIndexState.cs @@ -0,0 +1,24 @@ +using Neo.IO; +using Neo.VM; +using Neo.VM.Types; + +namespace Neo.SmartContract.Native +{ + class HashIndexState : IInteroperable + { + public UInt256 Hash; + public uint Index; + + void IInteroperable.FromStackItem(StackItem stackItem) + { + Struct @struct = (Struct)stackItem; + Hash = new UInt256(@struct[0].GetSpan()); + Index = (uint)@struct[1].GetInteger(); + } + + StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) { Hash.ToArray(), Index }; + } + } +} diff --git a/src/neo/SmartContract/Native/LedgerContract.cs b/src/neo/SmartContract/Native/LedgerContract.cs new file mode 100644 index 0000000000..2658584c7f --- /dev/null +++ b/src/neo/SmartContract/Native/LedgerContract.cs @@ -0,0 +1,205 @@ +#pragma warning disable IDE0051 + +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using System; +using System.Linq; +using System.Numerics; + +namespace Neo.SmartContract.Native +{ + public sealed class LedgerContract : NativeContract + { + private const byte Prefix_BlockHash = 9; + private const byte Prefix_CurrentBlock = 12; + private const byte Prefix_Block = 5; + private const byte Prefix_Transaction = 11; + + internal LedgerContract() + { + } + + internal override void OnPersist(ApplicationEngine engine) + { + engine.Snapshot.Add(CreateStorageKey(Prefix_BlockHash).AddBigEndian(engine.PersistingBlock.Index), new StorageItem(engine.PersistingBlock.Hash.ToArray(), true)); + engine.Snapshot.Add(CreateStorageKey(Prefix_Block).Add(engine.PersistingBlock.Hash), new StorageItem(Trim(engine.PersistingBlock).ToArray(), true)); + foreach (Transaction tx in engine.PersistingBlock.Transactions) + { + engine.Snapshot.Add(CreateStorageKey(Prefix_Transaction).Add(tx.Hash), new StorageItem(new TransactionState + { + BlockIndex = engine.PersistingBlock.Index, + Transaction = tx + }, true)); + } + } + + internal override void PostPersist(ApplicationEngine engine) + { + HashIndexState state = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_CurrentBlock), () => new StorageItem(new HashIndexState())).GetInteroperable(); + state.Hash = engine.PersistingBlock.Hash; + state.Index = engine.PersistingBlock.Index; + } + + internal bool Initialized(DataCache snapshot) + { + return snapshot.Find(CreateStorageKey(Prefix_Block).ToArray()).Any(); + } + + private bool IsTraceableBlock(DataCache snapshot, uint index) + { + uint currentIndex = CurrentIndex(snapshot); + if (index > currentIndex) return false; + return index + ProtocolSettings.Default.MaxTraceableBlocks > currentIndex; + } + + public UInt256 GetBlockHash(DataCache snapshot, uint index) + { + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_BlockHash).AddBigEndian(index)); + if (item is null) return null; + return new UInt256(item.Value); + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public UInt256 CurrentHash(DataCache snapshot) + { + return snapshot[CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable().Hash; + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + public uint CurrentIndex(DataCache snapshot) + { + return snapshot[CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable().Index; + } + + public bool ContainsBlock(DataCache snapshot, UInt256 hash) + { + return snapshot.Contains(CreateStorageKey(Prefix_Block).Add(hash)); + } + + public bool ContainsTransaction(DataCache snapshot, UInt256 hash) + { + return snapshot.Contains(CreateStorageKey(Prefix_Transaction).Add(hash)); + } + + public TrimmedBlock GetTrimmedBlock(DataCache snapshot, UInt256 hash) + { + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_Block).Add(hash)); + if (item is null) return null; + return item.Value.AsSerializable(); + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + private TrimmedBlock GetBlock(DataCache snapshot, byte[] indexOrHash) + { + UInt256 hash; + if (indexOrHash.Length < UInt256.Length) + hash = GetBlockHash(snapshot, (uint)new BigInteger(indexOrHash)); + else if (indexOrHash.Length == UInt256.Length) + hash = new UInt256(indexOrHash); + else + throw new ArgumentException(null, nameof(indexOrHash)); + if (hash is null) return null; + TrimmedBlock block = GetTrimmedBlock(snapshot, hash); + if (block is null || !IsTraceableBlock(snapshot, block.Index)) return null; + return block; + } + + public Block GetBlock(DataCache snapshot, UInt256 hash) + { + TrimmedBlock state = GetTrimmedBlock(snapshot, hash); + if (state is null) return null; + return new Block + { + Version = state.Version, + PrevHash = state.PrevHash, + MerkleRoot = state.MerkleRoot, + Timestamp = state.Timestamp, + Index = state.Index, + NextConsensus = state.NextConsensus, + Witness = state.Witness, + ConsensusData = state.ConsensusData, + Transactions = state.Hashes.Skip(1).Select(p => GetTransaction(snapshot, p)).ToArray() + }; + } + + public Block GetBlock(DataCache snapshot, uint index) + { + UInt256 hash = GetBlockHash(snapshot, index); + if (hash is null) return null; + return GetBlock(snapshot, hash); + } + + public Header GetHeader(DataCache snapshot, UInt256 hash) + { + return GetTrimmedBlock(snapshot, hash)?.Header; + } + + public Header GetHeader(DataCache snapshot, uint index) + { + UInt256 hash = GetBlockHash(snapshot, index); + if (hash is null) return null; + return GetHeader(snapshot, hash); + } + + public TransactionState GetTransactionState(DataCache snapshot, UInt256 hash) + { + return snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable(); + } + + public Transaction GetTransaction(DataCache snapshot, UInt256 hash) + { + return GetTransactionState(snapshot, hash)?.Transaction; + } + + [ContractMethod(0_01000000, CallFlags.ReadStates, Name = "getTransaction")] + private Transaction GetTransactionForContract(DataCache snapshot, UInt256 hash) + { + TransactionState state = GetTransactionState(snapshot, hash); + if (state is null || !IsTraceableBlock(snapshot, state.BlockIndex)) return null; + return state.Transaction; + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + private int GetTransactionHeight(DataCache snapshot, UInt256 hash) + { + TransactionState state = GetTransactionState(snapshot, hash); + if (state is null || !IsTraceableBlock(snapshot, state.BlockIndex)) return -1; + return (int)state.BlockIndex; + } + + [ContractMethod(0_02000000, CallFlags.ReadStates)] + private Transaction GetTransactionFromBlock(DataCache snapshot, byte[] blockIndexOrHash, int txIndex) + { + UInt256 hash; + if (blockIndexOrHash.Length < UInt256.Length) + hash = GetBlockHash(snapshot, (uint)new BigInteger(blockIndexOrHash)); + else if (blockIndexOrHash.Length == UInt256.Length) + hash = new UInt256(blockIndexOrHash); + else + throw new ArgumentException(null, nameof(blockIndexOrHash)); + if (hash is null) return null; + TrimmedBlock block = GetTrimmedBlock(snapshot, hash); + if (block is null || !IsTraceableBlock(snapshot, block.Index)) return null; + if (txIndex < 0 || txIndex >= block.Hashes.Length - 1) + throw new ArgumentOutOfRangeException(nameof(txIndex)); + return GetTransaction(snapshot, block.Hashes[txIndex + 1]); + } + + private static TrimmedBlock Trim(Block block) + { + return new TrimmedBlock + { + Version = block.Version, + PrevHash = block.PrevHash, + MerkleRoot = block.MerkleRoot, + Timestamp = block.Timestamp, + Index = block.Index, + NextConsensus = block.NextConsensus, + Witness = block.Witness, + Hashes = block.Transactions.Select(p => p.Hash).Prepend(block.ConsensusData.Hash).ToArray(), + ConsensusData = block.ConsensusData + }; + } + } +} diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index 409e0fe8bb..0c4463157d 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -2,7 +2,6 @@ using Neo.Cryptography; using Neo.IO; -using Neo.Ledger; using Neo.Persistence; using Neo.VM; using Neo.VM.Types; @@ -35,7 +34,7 @@ internal NameService() internal override void Initialize(ApplicationEngine engine) { - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_DomainPrice), new StorageItem(10_00000000)); + engine.Snapshot.Add(CreateStorageKey(Prefix_DomainPrice), new StorageItem(10_00000000)); } internal override void OnPersist(ApplicationEngine engine) @@ -43,11 +42,11 @@ internal override void OnPersist(ApplicationEngine engine) uint now = (uint)(engine.PersistingBlock.Timestamp / 1000) + 1; byte[] start = CreateStorageKey(Prefix_Expiration).AddBigEndian(0).ToArray(); byte[] end = CreateStorageKey(Prefix_Expiration).AddBigEndian(now).ToArray(); - foreach (var (key, _) in engine.Snapshot.Storages.FindRange(start, end)) + foreach (var (key, _) in engine.Snapshot.FindRange(start, end)) { - engine.Snapshot.Storages.Delete(key); - foreach (var (key2, _) in engine.Snapshot.Storages.Find(CreateStorageKey(Prefix_Record).Add(key.Key.AsSpan(5)).ToArray())) - engine.Snapshot.Storages.Delete(key2); + engine.Snapshot.Delete(key); + foreach (var (key2, _) in engine.Snapshot.Find(CreateStorageKey(Prefix_Record).Add(key.Key.AsSpan(5)).ToArray())) + engine.Snapshot.Delete(key2); Burn(engine, CreateStorageKey(Prefix_Token).Add(key.Key.AsSpan(5))); } } @@ -67,15 +66,15 @@ private void AddRoot(ApplicationEngine engine, string root) { if (!rootRegex.IsMatch(root)) throw new ArgumentException(null, nameof(root)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - StringList roots = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Roots), () => new StorageItem(new StringList())).GetInteroperable(); + StringList roots = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Roots), () => new StorageItem(new StringList())).GetInteroperable(); int index = roots.BinarySearch(root); if (index >= 0) throw new InvalidOperationException("The name already exists."); roots.Insert(~index, root); } - public IEnumerable GetRoots(StoreView snapshot) + public IEnumerable GetRoots(DataCache snapshot) { - return snapshot.Storages.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable() ?? Enumerable.Empty(); + return snapshot.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable() ?? Enumerable.Empty(); } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -83,24 +82,24 @@ private void SetPrice(ApplicationEngine engine, long price) { if (price <= 0 || price > 10000_00000000) throw new ArgumentOutOfRangeException(nameof(price)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_DomainPrice)).Set(price); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_DomainPrice)).Set(price); } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public long GetPrice(StoreView snapshot) + public long GetPrice(DataCache snapshot) { - return (long)(BigInteger)snapshot.Storages[CreateStorageKey(Prefix_DomainPrice)]; + return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_DomainPrice)]; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public bool IsAvailable(StoreView snapshot, string name) + public bool IsAvailable(DataCache snapshot, string name) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); string[] names = name.Split('.'); if (names.Length != 2) throw new ArgumentException(null, nameof(name)); byte[] hash = GetKey(Utility.StrictUTF8.GetBytes(name)); - if (snapshot.Storages.TryGet(CreateStorageKey(Prefix_Token).Add(hash)) is not null) return false; - StringList roots = snapshot.Storages.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable(); + if (snapshot.TryGet(CreateStorageKey(Prefix_Token).Add(hash)) is not null) return false; + StringList roots = snapshot.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable(); if (roots is null || roots.BinarySearch(names[1]) < 0) throw new InvalidOperationException(); return true; } @@ -113,8 +112,8 @@ private bool Register(ApplicationEngine engine, string name, UInt160 owner) if (names.Length != 2) throw new ArgumentException(null, nameof(name)); if (!engine.CheckWitnessInternal(owner)) throw new InvalidOperationException(); byte[] hash = GetKey(Utility.StrictUTF8.GetBytes(name)); - if (engine.Snapshot.Storages.TryGet(CreateStorageKey(Prefix_Token).Add(hash)) is not null) return false; - StringList roots = engine.Snapshot.Storages.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable(); + if (engine.Snapshot.TryGet(CreateStorageKey(Prefix_Token).Add(hash)) is not null) return false; + StringList roots = engine.Snapshot.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable(); if (roots is null || roots.BinarySearch(names[1]) < 0) throw new InvalidOperationException(); engine.AddGas(GetPrice(engine.Snapshot)); NameState state = new NameState @@ -125,7 +124,7 @@ private bool Register(ApplicationEngine engine, string name, UInt160 owner) Expiration = (uint)(engine.PersistingBlock.Timestamp / 1000) + OneYear }; Mint(engine, state); - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash), new StorageItem(new byte[] { 0 })); + engine.Snapshot.Add(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash), new StorageItem(new byte[] { 0 })); return true; } @@ -137,10 +136,10 @@ private uint Renew(ApplicationEngine engine, string name) if (names.Length != 2) throw new ArgumentException(null, nameof(name)); engine.AddGas(GetPrice(engine.Snapshot)); byte[] hash = GetKey(Utility.StrictUTF8.GetBytes(name)); - NameState state = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Token).Add(hash)).GetInteroperable(); - engine.Snapshot.Storages.Delete(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash)); + NameState state = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Token).Add(hash)).GetInteroperable(); + engine.Snapshot.Delete(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash)); state.Expiration += OneYear; - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash), new StorageItem(new byte[] { 0 })); + engine.Snapshot.Add(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash), new StorageItem(new byte[] { 0 })); return state.Expiration; } @@ -151,7 +150,7 @@ private void SetAdmin(ApplicationEngine engine, string name, UInt160 admin) string[] names = name.Split('.'); if (names.Length != 2) throw new ArgumentException(null, nameof(name)); if (admin != null && !engine.CheckWitnessInternal(admin)) throw new InvalidOperationException(); - NameState state = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Token).Add(GetKey(Utility.StrictUTF8.GetBytes(name)))).GetInteroperable(); + NameState state = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Token).Add(GetKey(Utility.StrictUTF8.GetBytes(name)))).GetInteroperable(); if (!engine.CheckWitnessInternal(state.Owner)) throw new InvalidOperationException(); state.Admin = admin; } @@ -188,29 +187,29 @@ private void SetRecord(ApplicationEngine engine, string name, RecordType type, s } string domain = string.Join('.', name.Split('.')[^2..]); byte[] hash_domain = GetKey(Utility.StrictUTF8.GetBytes(domain)); - NameState state = engine.Snapshot.Storages[CreateStorageKey(Prefix_Token).Add(hash_domain)].GetInteroperable(); + NameState state = engine.Snapshot[CreateStorageKey(Prefix_Token).Add(hash_domain)].GetInteroperable(); if (!CheckAdmin(engine, state)) throw new InvalidOperationException(); - StorageItem item = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type), () => new StorageItem()); + StorageItem item = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type), () => new StorageItem()); item.Value = Utility.StrictUTF8.GetBytes(data); } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public string GetRecord(StoreView snapshot, string name, RecordType type) + public string GetRecord(DataCache snapshot, string name, RecordType type) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); string domain = string.Join('.', name.Split('.')[^2..]); byte[] hash_domain = GetKey(Utility.StrictUTF8.GetBytes(domain)); - StorageItem item = snapshot.Storages.TryGet(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type)); + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type)); if (item is null) return null; return Utility.StrictUTF8.GetString(item.Value); } - public IEnumerable<(RecordType Type, string Data)> GetRecords(StoreView snapshot, string name) + public IEnumerable<(RecordType Type, string Data)> GetRecords(DataCache snapshot, string name) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); string domain = string.Join('.', name.Split('.')[^2..]); byte[] hash_domain = GetKey(Utility.StrictUTF8.GetBytes(domain)); - foreach (var (key, value) in snapshot.Storages.Find(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).ToArray())) + foreach (var (key, value) in snapshot.Find(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).ToArray())) yield return ((RecordType)key.Key[^1], Utility.StrictUTF8.GetString(value.Value)); } @@ -220,18 +219,18 @@ private void DeleteRecord(ApplicationEngine engine, string name, RecordType type if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); string domain = string.Join('.', name.Split('.')[^2..]); byte[] hash_domain = GetKey(Utility.StrictUTF8.GetBytes(domain)); - NameState state = engine.Snapshot.Storages[CreateStorageKey(Prefix_Token).Add(hash_domain)].GetInteroperable(); + NameState state = engine.Snapshot[CreateStorageKey(Prefix_Token).Add(hash_domain)].GetInteroperable(); if (!CheckAdmin(engine, state)) throw new InvalidOperationException(); - engine.Snapshot.Storages.Delete(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type)); + engine.Snapshot.Delete(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type)); } [ContractMethod(0_03000000, CallFlags.ReadStates)] - public string Resolve(StoreView snapshot, string name, RecordType type) + public string Resolve(DataCache snapshot, string name, RecordType type) { return Resolve(snapshot, name, type, 2); } - private string Resolve(StoreView snapshot, string name, RecordType type, int redirect) + private string Resolve(DataCache snapshot, string name, RecordType type, int redirect) { if (redirect < 0) throw new InvalidOperationException(); var dictionary = GetRecords(snapshot, name).ToDictionary(p => p.Type, p => p.Data); diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index f887e63d0e..4796d2a96a 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -21,6 +21,7 @@ public abstract class NativeContract #region Named Native Contracts public static ContractManagement ContractManagement { get; } = new ContractManagement(); + public static LedgerContract Ledger { get; } = new LedgerContract(); public static NeoToken NEO { get; } = new NeoToken(); public static GasToken GAS { get; } = new GasToken(); public static PolicyContract Policy { get; } = new PolicyContract(); diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index b95f8eae89..e050e16877 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -2,7 +2,6 @@ using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Caching; using Neo.Ledger; using Neo.Persistence; using Neo.VM; @@ -38,7 +37,7 @@ internal NeoToken() this.TotalAmount = 100000000 * Factor; } - public override BigInteger TotalSupply(StoreView snapshot) + public override BigInteger TotalSupply(DataCache snapshot) { return TotalAmount; } @@ -48,9 +47,9 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco DistributeGas(engine, account, state); if (amount.IsZero) return; if (state.VoteTo is null) return; - engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_VotersCount)).Add(amount); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_VotersCount)).Add(amount); StorageKey key = CreateStorageKey(Prefix_Candidate).Add(state.VoteTo); - CandidateState candidate = engine.Snapshot.Storages.GetAndChange(key).GetInteroperable(); + CandidateState candidate = engine.Snapshot.GetAndChange(key).GetInteroperable(); candidate.Votes += amount; CheckCandidate(engine.Snapshot, state.VoteTo, candidate); } @@ -65,7 +64,7 @@ private void DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccount GAS.Mint(engine, account, gas, true); } - private BigInteger CalculateBonus(StoreView snapshot, ECPoint vote, BigInteger value, uint start, uint end) + private BigInteger CalculateBonus(DataCache snapshot, ECPoint vote, BigInteger value, uint start, uint end) { if (value.IsZero || start >= end) return BigInteger.Zero; if (value.Sign < 0) throw new ArgumentOutOfRangeException(nameof(value)); @@ -75,17 +74,17 @@ private BigInteger CalculateBonus(StoreView snapshot, ECPoint vote, BigInteger v byte[] border = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(vote).ToArray(); byte[] keyStart = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(vote).AddBigEndian(start).ToArray(); - (_, var item) = snapshot.Storages.FindRange(keyStart, border, SeekDirection.Backward).FirstOrDefault(); + (_, var item) = snapshot.FindRange(keyStart, border, SeekDirection.Backward).FirstOrDefault(); BigInteger startRewardPerNeo = item ?? BigInteger.Zero; byte[] keyEnd = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(vote).AddBigEndian(end).ToArray(); - (_, item) = snapshot.Storages.FindRange(keyEnd, border, SeekDirection.Backward).FirstOrDefault(); + (_, item) = snapshot.FindRange(keyEnd, border, SeekDirection.Backward).FirstOrDefault(); BigInteger endRewardPerNeo = item ?? BigInteger.Zero; return neoHolderReward + value * (endRewardPerNeo - startRewardPerNeo) / 100000000L; } - private BigInteger CalculateNeoHolderReward(StoreView snapshot, BigInteger value, uint start, uint end) + private BigInteger CalculateNeoHolderReward(DataCache snapshot, BigInteger value, uint start, uint end) { BigInteger sum = 0; foreach (var (index, gasPerBlock) in GetSortedGasRecords(snapshot, end - 1)) @@ -104,13 +103,13 @@ private BigInteger CalculateNeoHolderReward(StoreView snapshot, BigInteger value return value * sum * NeoHolderRewardRatio / 100 / TotalAmount; } - private void CheckCandidate(StoreView snapshot, ECPoint pubkey, CandidateState candidate) + private void CheckCandidate(DataCache snapshot, ECPoint pubkey, CandidateState candidate) { if (!candidate.Registered && candidate.Votes.IsZero) { - foreach (var (rewardKey, _) in snapshot.Storages.Find(CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(pubkey).ToArray()).ToArray()) - snapshot.Storages.Delete(rewardKey); - snapshot.Storages.Delete(CreateStorageKey(Prefix_Candidate).Add(pubkey)); + foreach (var (rewardKey, _) in snapshot.Find(CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(pubkey).ToArray()).ToArray()) + snapshot.Delete(rewardKey); + snapshot.Delete(CreateStorageKey(Prefix_Candidate).Add(pubkey)); } } @@ -119,13 +118,13 @@ private void CheckCandidate(StoreView snapshot, ECPoint pubkey, CandidateState c internal override void Initialize(ApplicationEngine engine) { var cachedCommittee = new CachedCommittee(Blockchain.StandbyCommittee.Select(p => (p, BigInteger.Zero))); - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee)); - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(new byte[0])); + engine.Snapshot.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee)); + engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(new byte[0])); // Initialize economic parameters - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor)); - Mint(engine, Blockchain.GetConsensusAddress(Blockchain.StandbyValidators), TotalAmount, false); + engine.Snapshot.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor)); + Mint(engine, Contract.GetBFTAddress(Blockchain.StandbyValidators), TotalAmount, false); } internal override void OnPersist(ApplicationEngine engine) @@ -133,7 +132,7 @@ internal override void OnPersist(ApplicationEngine engine) // Set next committee if (ShouldRefreshCommittee(engine.PersistingBlock.Index)) { - StorageItem storageItem = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Committee)); + StorageItem storageItem = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Committee)); var cachedCommittee = storageItem.GetInteroperable(); cachedCommittee.Clear(); cachedCommittee.AddRange(ComputeCommitteeMembers(engine.Snapshot)); @@ -167,9 +166,9 @@ internal override void PostPersist(ApplicationEngine engine) BigInteger voterSumRewardPerNEO = factor * voterRewardOfEachCommittee / member.Votes; StorageKey voterRewardKey = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(member.PublicKey).AddBigEndian(engine.PersistingBlock.Index + 1); byte[] border = CreateStorageKey(Prefix_VoterRewardPerCommittee).Add(member.PublicKey).ToArray(); - (_, var item) = engine.Snapshot.Storages.FindRange(voterRewardKey.ToArray(), border, SeekDirection.Backward).FirstOrDefault(); + (_, var item) = engine.Snapshot.FindRange(voterRewardKey.ToArray(), border, SeekDirection.Backward).FirstOrDefault(); voterSumRewardPerNEO += (item ?? BigInteger.Zero); - engine.Snapshot.Storages.Add(voterRewardKey, new StorageItem(voterSumRewardPerNEO)); + engine.Snapshot.Add(voterRewardKey, new StorageItem(voterSumRewardPerNEO)); } } } @@ -183,29 +182,29 @@ private bool SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) if (!CheckCommittee(engine)) return false; uint index = engine.PersistingBlock.Index + 1; - StorageItem entry = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(index), () => new StorageItem(gasPerBlock)); + StorageItem entry = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(index), () => new StorageItem(gasPerBlock)); entry.Set(gasPerBlock); return true; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public BigInteger GetGasPerBlock(StoreView snapshot) + public BigInteger GetGasPerBlock(DataCache snapshot) { - return GetSortedGasRecords(snapshot, snapshot.Height + 1).First().GasPerBlock; + return GetSortedGasRecords(snapshot, Ledger.CurrentIndex(snapshot) + 1).First().GasPerBlock; } - private IEnumerable<(uint Index, BigInteger GasPerBlock)> GetSortedGasRecords(StoreView snapshot, uint end) + private IEnumerable<(uint Index, BigInteger GasPerBlock)> GetSortedGasRecords(DataCache snapshot, uint end) { byte[] key = CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(end).ToArray(); byte[] boundary = CreateStorageKey(Prefix_GasPerBlock).ToArray(); - return snapshot.Storages.FindRange(key, boundary, SeekDirection.Backward) + return snapshot.FindRange(key, boundary, SeekDirection.Backward) .Select(u => (BinaryPrimitives.ReadUInt32BigEndian(u.Key.Key.AsSpan(^sizeof(uint))), (BigInteger)u.Value)); } [ContractMethod(0_03000000, CallFlags.ReadStates)] - public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) + public BigInteger UnclaimedGas(DataCache snapshot, UInt160 account, uint end) { - StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_Account).Add(account)); + StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(account)); if (storage is null) return BigInteger.Zero; NeoAccountState state = storage.GetInteroperable(); return CalculateBonus(snapshot, state.VoteTo, state.Balance, state.BalanceHeight, end); @@ -217,7 +216,7 @@ private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; StorageKey key = CreateStorageKey(Prefix_Candidate).Add(pubkey); - StorageItem item = engine.Snapshot.Storages.GetAndChange(key, () => new StorageItem(new CandidateState())); + StorageItem item = engine.Snapshot.GetAndChange(key, () => new StorageItem(new CandidateState())); CandidateState state = item.GetInteroperable(); state.Registered = true; return true; @@ -229,8 +228,8 @@ private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; StorageKey key = CreateStorageKey(Prefix_Candidate).Add(pubkey); - if (engine.Snapshot.Storages.TryGet(key) is null) return true; - StorageItem item = engine.Snapshot.Storages.GetAndChange(key); + if (engine.Snapshot.TryGet(key) is null) return true; + StorageItem item = engine.Snapshot.GetAndChange(key); CandidateState state = item.GetInteroperable(); state.Registered = false; CheckCandidate(engine.Snapshot, pubkey, state); @@ -241,18 +240,18 @@ private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) { if (!engine.CheckWitnessInternal(account)) return false; - NeoAccountState state_account = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Account).Add(account))?.GetInteroperable(); + NeoAccountState state_account = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Account).Add(account))?.GetInteroperable(); if (state_account is null) return false; CandidateState validator_new = null; if (voteTo != null) { - validator_new = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Candidate).Add(voteTo))?.GetInteroperable(); + validator_new = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Candidate).Add(voteTo))?.GetInteroperable(); if (validator_new is null) return false; if (!validator_new.Registered) return false; } if (state_account.VoteTo is null ^ voteTo is null) { - StorageItem item = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_VotersCount)); + StorageItem item = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_VotersCount)); if (state_account.VoteTo is null) item.Add(state_account.Balance); else @@ -262,7 +261,7 @@ private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) if (state_account.VoteTo != null) { StorageKey key = CreateStorageKey(Prefix_Candidate).Add(state_account.VoteTo); - StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(key); + StorageItem storage_validator = engine.Snapshot.GetAndChange(key); CandidateState state_validator = storage_validator.GetInteroperable(); state_validator.Votes -= state_account.Balance; CheckCandidate(engine.Snapshot, state_account.VoteTo, state_validator); @@ -276,10 +275,10 @@ private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) } [ContractMethod(1_00000000, CallFlags.ReadStates)] - public (ECPoint PublicKey, BigInteger Votes)[] GetCandidates(StoreView snapshot) + public (ECPoint PublicKey, BigInteger Votes)[] GetCandidates(DataCache snapshot) { byte[] prefix_key = CreateStorageKey(Prefix_Candidate).ToArray(); - return snapshot.Storages.Find(prefix_key).Select(p => + return snapshot.Find(prefix_key).Select(p => ( p.Key.Key.AsSerializable(1), p.Value.GetInteroperable() @@ -287,30 +286,30 @@ private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) } [ContractMethod(1_00000000, CallFlags.ReadStates)] - public ECPoint[] GetCommittee(StoreView snapshot) + public ECPoint[] GetCommittee(DataCache snapshot) { return GetCommitteeFromCache(snapshot).Select(p => p.PublicKey).OrderBy(p => p).ToArray(); } - public UInt160 GetCommitteeAddress(StoreView snapshot) + public UInt160 GetCommitteeAddress(DataCache snapshot) { ECPoint[] committees = GetCommittee(snapshot); return Contract.CreateMultiSigRedeemScript(committees.Length - (committees.Length - 1) / 2, committees).ToScriptHash(); } - private CachedCommittee GetCommitteeFromCache(StoreView snapshot) + private CachedCommittee GetCommitteeFromCache(DataCache snapshot) { - return snapshot.Storages[CreateStorageKey(Prefix_Committee)].GetInteroperable(); + return snapshot[CreateStorageKey(Prefix_Committee)].GetInteroperable(); } - public ECPoint[] ComputeNextBlockValidators(StoreView snapshot) + public ECPoint[] ComputeNextBlockValidators(DataCache snapshot) { return ComputeCommitteeMembers(snapshot).Select(p => p.PublicKey).Take(ProtocolSettings.Default.ValidatorsCount).OrderBy(p => p).ToArray(); } - private IEnumerable<(ECPoint PublicKey, BigInteger Votes)> ComputeCommitteeMembers(StoreView snapshot) + private IEnumerable<(ECPoint PublicKey, BigInteger Votes)> ComputeCommitteeMembers(DataCache snapshot) { - decimal votersCount = (decimal)(BigInteger)snapshot.Storages[CreateStorageKey(Prefix_VotersCount)]; + decimal votersCount = (decimal)(BigInteger)snapshot[CreateStorageKey(Prefix_VotersCount)]; decimal voterTurnout = votersCount / (decimal)TotalAmount; var candidates = GetCandidates(snapshot); if (voterTurnout < EffectiveVoterTurnout || candidates.Length < ProtocolSettings.Default.CommitteeMembersCount) @@ -319,7 +318,7 @@ public ECPoint[] ComputeNextBlockValidators(StoreView snapshot) } [ContractMethod(1_00000000, CallFlags.ReadStates)] - public ECPoint[] GetNextBlockValidators(StoreView snapshot) + public ECPoint[] GetNextBlockValidators(DataCache snapshot) { return GetCommitteeFromCache(snapshot) .Take(ProtocolSettings.Default.ValidatorsCount) diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index c77fbc70fe..8fa3696fe5 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract.Iterators; using Neo.SmartContract.Manifest; @@ -65,10 +64,10 @@ protected NonfungibleToken() protected void Mint(ApplicationEngine engine, TokenState token) { - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Token).Add(GetKey(token.Id)), new StorageItem(token)); - NFTAccountState account = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Account).Add(token.Owner), () => new StorageItem(new NFTAccountState())).GetInteroperable(); + engine.Snapshot.Add(CreateStorageKey(Prefix_Token).Add(GetKey(token.Id)), new StorageItem(token)); + NFTAccountState account = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Account).Add(token.Owner), () => new StorageItem(new NFTAccountState())).GetInteroperable(); account.Add(token.Id); - engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero)).Add(1); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero)).Add(1); PostTransfer(engine, null, token.Owner, token.Id); } @@ -79,55 +78,55 @@ protected void Burn(ApplicationEngine engine, byte[] tokenId) private protected void Burn(ApplicationEngine engine, StorageKey key) { - TokenState token = engine.Snapshot.Storages.TryGet(key)?.GetInteroperable(); + TokenState token = engine.Snapshot.TryGet(key)?.GetInteroperable(); if (token is null) throw new InvalidOperationException(); - engine.Snapshot.Storages.Delete(key); + engine.Snapshot.Delete(key); StorageKey key_account = CreateStorageKey(Prefix_Account).Add(token.Owner); - NFTAccountState account = engine.Snapshot.Storages.GetAndChange(key_account).GetInteroperable(); + NFTAccountState account = engine.Snapshot.GetAndChange(key_account).GetInteroperable(); account.Remove(token.Id); if (account.Balance.IsZero) - engine.Snapshot.Storages.Delete(key_account); - engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply)).Add(-1); + engine.Snapshot.Delete(key_account); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply)).Add(-1); PostTransfer(engine, token.Owner, null, token.Id); } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public BigInteger TotalSupply(StoreView snapshot) + public BigInteger TotalSupply(DataCache snapshot) { - StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_TotalSupply)); + StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_TotalSupply)); return storage ?? BigInteger.Zero; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public UInt160 OwnerOf(StoreView snapshot, byte[] tokenId) + public UInt160 OwnerOf(DataCache snapshot, byte[] tokenId) { - return snapshot.Storages[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().Owner; + return snapshot[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().Owner; } [ContractMethod(0_01000000, CallFlags.ReadStates)] public Map Properties(ApplicationEngine engine, byte[] tokenId) { - return engine.Snapshot.Storages[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().ToMap(engine.ReferenceCounter); + return engine.Snapshot[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().ToMap(engine.ReferenceCounter); } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public BigInteger BalanceOf(StoreView snapshot, UInt160 owner) + public BigInteger BalanceOf(DataCache snapshot, UInt160 owner) { if (owner is null) throw new ArgumentNullException(nameof(owner)); - return snapshot.Storages.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable().Balance ?? BigInteger.Zero; + return snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable().Balance ?? BigInteger.Zero; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public IIterator TokensOf(StoreView snapshot, UInt160 owner) + public IIterator TokensOf(DataCache snapshot, UInt160 owner) { if (owner is null) { - var results = snapshot.Storages.Find(new[] { Prefix_Token }).GetEnumerator(); + var results = snapshot.Find(new[] { Prefix_Token }).GetEnumerator(); return new StorageIterator(results, FindOptions.ValuesOnly | FindOptions.DeserializeValues | FindOptions.PickField1, null); } else { - NFTAccountState account = snapshot.Storages.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable(); + NFTAccountState account = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable(); IReadOnlyList tokens = account?.Tokens ?? (IReadOnlyList)System.Array.Empty(); return new ArrayWrapper(tokens.Select(p => (StackItem)p).ToArray()); } @@ -138,21 +137,21 @@ protected bool Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) { if (to is null) throw new ArgumentNullException(nameof(to)); StorageKey key_token = CreateStorageKey(Prefix_Token).Add(GetKey(tokenId)); - TokenState token = engine.Snapshot.Storages.TryGet(key_token)?.GetInteroperable(); + TokenState token = engine.Snapshot.TryGet(key_token)?.GetInteroperable(); UInt160 from = token.Owner; if (!from.Equals(engine.CallingScriptHash) && !engine.CheckWitnessInternal(from)) return false; if (!from.Equals(to)) { - token = engine.Snapshot.Storages.GetAndChange(key_token).GetInteroperable(); + token = engine.Snapshot.GetAndChange(key_token).GetInteroperable(); StorageKey key_from = CreateStorageKey(Prefix_Account).Add(from); - NFTAccountState account = engine.Snapshot.Storages.GetAndChange(key_from).GetInteroperable(); + NFTAccountState account = engine.Snapshot.GetAndChange(key_from).GetInteroperable(); account.Remove(tokenId); if (account.Balance.IsZero) - engine.Snapshot.Storages.Delete(key_from); + engine.Snapshot.Delete(key_from); token.Owner = to; StorageKey key_to = CreateStorageKey(Prefix_Account).Add(to); - account = engine.Snapshot.Storages.GetAndChange(key_to, () => new StorageItem(new NFTAccountState())).GetInteroperable(); + account = engine.Snapshot.GetAndChange(key_to, () => new StorageItem(new NFTAccountState())).GetInteroperable(); account.Add(tokenId); OnTransferred(engine, from, token); } diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index b660f347ff..5e4e710de0 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -2,7 +2,6 @@ using Neo.Cryptography; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; @@ -103,22 +102,22 @@ private UInt256 GetOriginalTxid(ApplicationEngine engine) return request.OriginalTxid; } - public OracleRequest GetRequest(StoreView snapshot, ulong id) + public OracleRequest GetRequest(DataCache snapshot, ulong id) { - return snapshot.Storages.TryGet(CreateStorageKey(Prefix_Request).Add(id))?.GetInteroperable(); + return snapshot.TryGet(CreateStorageKey(Prefix_Request).Add(id))?.GetInteroperable(); } - public IEnumerable<(ulong, OracleRequest)> GetRequests(StoreView snapshot) + public IEnumerable<(ulong, OracleRequest)> GetRequests(DataCache snapshot) { - return snapshot.Storages.Find(new KeyBuilder(Id, Prefix_Request).ToArray()).Select(p => (BitConverter.ToUInt64(p.Key.Key, 1), p.Value.GetInteroperable())); + return snapshot.Find(new KeyBuilder(Id, Prefix_Request).ToArray()).Select(p => (BitConverter.ToUInt64(p.Key.Key, 1), p.Value.GetInteroperable())); } - public IEnumerable<(ulong, OracleRequest)> GetRequestsByUrl(StoreView snapshot, string url) + public IEnumerable<(ulong, OracleRequest)> GetRequestsByUrl(DataCache snapshot, string url) { - IdList list = snapshot.Storages.TryGet(CreateStorageKey(Prefix_IdList).Add(GetUrlHash(url)))?.GetInteroperable(); + IdList list = snapshot.TryGet(CreateStorageKey(Prefix_IdList).Add(GetUrlHash(url)))?.GetInteroperable(); if (list is null) yield break; foreach (ulong id in list) - yield return (id, snapshot.Storages[CreateStorageKey(Prefix_Request).Add(id)].GetInteroperable()); + yield return (id, snapshot[CreateStorageKey(Prefix_Request).Add(id)].GetInteroperable()); } private static byte[] GetUrlHash(string url) @@ -128,7 +127,7 @@ private static byte[] GetUrlHash(string url) internal override void Initialize(ApplicationEngine engine) { - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BitConverter.GetBytes(0ul))); + engine.Snapshot.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BitConverter.GetBytes(0ul))); } internal override void PostPersist(ApplicationEngine engine) @@ -142,15 +141,15 @@ internal override void PostPersist(ApplicationEngine engine) //Remove the request from storage StorageKey key = CreateStorageKey(Prefix_Request).Add(response.Id); - OracleRequest request = engine.Snapshot.Storages.TryGet(key)?.GetInteroperable(); + OracleRequest request = engine.Snapshot.TryGet(key)?.GetInteroperable(); if (request == null) continue; - engine.Snapshot.Storages.Delete(key); + engine.Snapshot.Delete(key); //Remove the id from IdList key = CreateStorageKey(Prefix_IdList).Add(GetUrlHash(request.Url)); - IdList list = engine.Snapshot.Storages.GetAndChange(key).GetInteroperable(); + IdList list = engine.Snapshot.GetAndChange(key).GetInteroperable(); if (!list.Remove(response.Id)) throw new InvalidOperationException(); - if (list.Count == 0) engine.Snapshot.Storages.Delete(key); + if (list.Count == 0) engine.Snapshot.Delete(key); //Mint GAS for oracle nodes nodes ??= RoleManagement.GetDesignatedByRole(engine.Snapshot, Role.Oracle, engine.PersistingBlock.Index).Select(p => (Contract.CreateSignatureRedeemScript(p).ToScriptHash(), BigInteger.Zero)).ToArray(); @@ -184,14 +183,14 @@ private void Request(ApplicationEngine engine, string url, string filter, string GAS.Mint(engine, Hash, gasForResponse, false); //Increase the request id - StorageItem item_id = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_RequestId)); + StorageItem item_id = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_RequestId)); ulong id = BitConverter.ToUInt64(item_id.Value) + 1; item_id.Value = BitConverter.GetBytes(id); //Put the request to storage if (ContractManagement.GetContract(engine.Snapshot, engine.CallingScriptHash) is null) throw new InvalidOperationException(); - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Request).Add(item_id.Value), new StorageItem(new OracleRequest + engine.Snapshot.Add(CreateStorageKey(Prefix_Request).Add(item_id.Value), new StorageItem(new OracleRequest { OriginalTxid = GetOriginalTxid(engine), GasForResponse = gasForResponse, @@ -203,7 +202,7 @@ private void Request(ApplicationEngine engine, string url, string filter, string })); //Add the id to the IdList - var list = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_IdList).Add(GetUrlHash(url)), () => new StorageItem(new IdList())).GetInteroperable(); + var list = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_IdList).Add(GetUrlHash(url)), () => new StorageItem(new IdList())).GetInteroperable(); if (list.Count >= 256) throw new InvalidOperationException("There are too many pending responses for this url"); list.Add(id); diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index c873f4ce14..0b5b7a20c3 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -1,6 +1,5 @@ #pragma warning disable IDE0051 -using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -29,57 +28,57 @@ internal PolicyContract() } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public uint GetMaxTransactionsPerBlock(StoreView snapshot) + public uint GetMaxTransactionsPerBlock(DataCache snapshot) { - StorageItem item = snapshot.Storages.TryGet(CreateStorageKey(Prefix_MaxTransactionsPerBlock)); + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_MaxTransactionsPerBlock)); if (item is null) return 512; return (uint)(BigInteger)item; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public uint GetMaxBlockSize(StoreView snapshot) + public uint GetMaxBlockSize(DataCache snapshot) { - StorageItem item = snapshot.Storages.TryGet(CreateStorageKey(Prefix_MaxBlockSize)); + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_MaxBlockSize)); if (item is null) return 1024 * 256; return (uint)(BigInteger)item; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public long GetMaxBlockSystemFee(StoreView snapshot) + public long GetMaxBlockSystemFee(DataCache snapshot) { - StorageItem item = snapshot.Storages.TryGet(CreateStorageKey(Prefix_MaxBlockSystemFee)); + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_MaxBlockSystemFee)); if (item is null) return 9000 * (long)GAS.Factor; // For the transfer method of NEP5, the maximum persisting time is about three seconds. return (long)(BigInteger)item; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public long GetFeePerByte(StoreView snapshot) + public long GetFeePerByte(DataCache snapshot) { - StorageItem item = snapshot.Storages.TryGet(CreateStorageKey(Prefix_FeePerByte)); + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_FeePerByte)); if (item is null) return 1000; return (long)(BigInteger)item; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public uint GetExecFeeFactor(StoreView snapshot) + public uint GetExecFeeFactor(DataCache snapshot) { - StorageItem item = snapshot.Storages.TryGet(CreateStorageKey(Prefix_ExecFeeFactor)); + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_ExecFeeFactor)); if (item is null) return DefaultExecFeeFactor; return (uint)(BigInteger)item; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public uint GetStoragePrice(StoreView snapshot) + public uint GetStoragePrice(DataCache snapshot) { - StorageItem item = snapshot.Storages.TryGet(CreateStorageKey(Prefix_StoragePrice)); + StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_StoragePrice)); if (item is null) return DefaultStoragePrice; return (uint)(BigInteger)item; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public bool IsBlocked(StoreView snapshot, UInt160 account) + public bool IsBlocked(DataCache snapshot, UInt160 account) { - return snapshot.Storages.Contains(CreateStorageKey(Prefix_BlockedAccount).Add(account)); + return snapshot.Contains(CreateStorageKey(Prefix_BlockedAccount).Add(account)); } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -87,7 +86,7 @@ private bool SetMaxBlockSize(ApplicationEngine engine, uint value) { if (value > Message.PayloadMaxSize) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) return false; - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize), () => new StorageItem()); + StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize), () => new StorageItem()); storage.Set(value); return true; } @@ -97,7 +96,7 @@ private bool SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value) { if (value > Block.MaxTransactionsPerBlock) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) return false; - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock), () => new StorageItem()); + StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock), () => new StorageItem()); storage.Set(value); return true; } @@ -107,7 +106,7 @@ private bool SetMaxBlockSystemFee(ApplicationEngine engine, long value) { if (value <= 4007600) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) return false; - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSystemFee), () => new StorageItem()); + StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSystemFee), () => new StorageItem()); storage.Set(value); return true; } @@ -117,7 +116,7 @@ private bool SetFeePerByte(ApplicationEngine engine, long value) { if (value < 0 || value > 1_00000000) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) return false; - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_FeePerByte), () => new StorageItem()); + StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_FeePerByte), () => new StorageItem()); storage.Set(value); return true; } @@ -127,7 +126,7 @@ private bool SetExecFeeFactor(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxExecFeeFactor) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) return false; - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_ExecFeeFactor), () => new StorageItem()); + StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_ExecFeeFactor), () => new StorageItem()); storage.Set(value); return true; } @@ -137,7 +136,7 @@ private bool SetStoragePrice(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxStoragePrice) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) return false; - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_StoragePrice), () => new StorageItem()); + StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_StoragePrice), () => new StorageItem()); storage.Set(value); return true; } @@ -148,9 +147,9 @@ private bool BlockAccount(ApplicationEngine engine, UInt160 account) if (!CheckCommittee(engine)) return false; var key = CreateStorageKey(Prefix_BlockedAccount).Add(account); - if (engine.Snapshot.Storages.Contains(key)) return false; + if (engine.Snapshot.Contains(key)) return false; - engine.Snapshot.Storages.Add(key, new StorageItem(new byte[] { 0x01 })); + engine.Snapshot.Add(key, new StorageItem(new byte[] { 0x01 })); return true; } @@ -160,9 +159,9 @@ private bool UnblockAccount(ApplicationEngine engine, UInt160 account) if (!CheckCommittee(engine)) return false; var key = CreateStorageKey(Prefix_BlockedAccount).Add(account); - if (!engine.Snapshot.Storages.Contains(key)) return false; + if (!engine.Snapshot.Contains(key)) return false; - engine.Snapshot.Storages.Delete(key); + engine.Snapshot.Delete(key); return true; } } diff --git a/src/neo/SmartContract/Native/RoleManagement.cs b/src/neo/SmartContract/Native/RoleManagement.cs index ee95bd3edb..6c79093139 100644 --- a/src/neo/SmartContract/Native/RoleManagement.cs +++ b/src/neo/SmartContract/Native/RoleManagement.cs @@ -1,8 +1,6 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Caching; -using Neo.Ledger; using Neo.Persistence; using Neo.VM; using Neo.VM.Types; @@ -19,15 +17,15 @@ internal RoleManagement() } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public ECPoint[] GetDesignatedByRole(StoreView snapshot, Role role, uint index) + public ECPoint[] GetDesignatedByRole(DataCache snapshot, Role role, uint index) { if (!Enum.IsDefined(typeof(Role), role)) throw new ArgumentOutOfRangeException(nameof(role)); - if (snapshot.Height + 1 < index) + if (Ledger.CurrentIndex(snapshot) + 1 < index) throw new ArgumentOutOfRangeException(nameof(index)); byte[] key = CreateStorageKey((byte)role).AddBigEndian(index).ToArray(); byte[] boundary = CreateStorageKey((byte)role).ToArray(); - return snapshot.Storages.FindRange(key, boundary, SeekDirection.Backward) + return snapshot.FindRange(key, boundary, SeekDirection.Backward) .Select(u => u.Value.GetInteroperable().ToArray()) .FirstOrDefault() ?? System.Array.Empty(); } @@ -45,12 +43,12 @@ private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] node throw new InvalidOperationException(nameof(DesignateAsRole)); uint index = engine.PersistingBlock.Index + 1; var key = CreateStorageKey((byte)role).AddBigEndian(index); - if (engine.Snapshot.Storages.Contains(key)) + if (engine.Snapshot.Contains(key)) throw new InvalidOperationException(); NodeList list = new NodeList(); list.AddRange(nodes); list.Sort(); - engine.Snapshot.Storages.Add(key, new StorageItem(list)); + engine.Snapshot.Add(key, new StorageItem(list)); } private class NodeList : List, IInteroperable diff --git a/src/neo/SmartContract/Native/TransactionState.cs b/src/neo/SmartContract/Native/TransactionState.cs new file mode 100644 index 0000000000..671146a6de --- /dev/null +++ b/src/neo/SmartContract/Native/TransactionState.cs @@ -0,0 +1,29 @@ +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.VM; +using Neo.VM.Types; + +namespace Neo.SmartContract.Native +{ + public class TransactionState : IInteroperable + { + public uint BlockIndex; + public Transaction Transaction; + + private StackItem _rawTransaction; + + void IInteroperable.FromStackItem(StackItem stackItem) + { + Struct @struct = (Struct)stackItem; + BlockIndex = (uint)@struct[0].GetInteger(); + _rawTransaction = @struct[1]; + Transaction = _rawTransaction.GetSpan().AsSerializable(); + } + + StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + { + _rawTransaction ??= Transaction.ToArray(); + return new Struct(referenceCounter) { BlockIndex, _rawTransaction }; + } + } +} diff --git a/src/neo/Ledger/TrimmedBlock.cs b/src/neo/SmartContract/Native/TrimmedBlock.cs similarity index 52% rename from src/neo/Ledger/TrimmedBlock.cs rename to src/neo/SmartContract/Native/TrimmedBlock.cs index 5b58e40246..11133fb9d7 100644 --- a/src/neo/Ledger/TrimmedBlock.cs +++ b/src/neo/SmartContract/Native/TrimmedBlock.cs @@ -1,35 +1,19 @@ using Neo.IO; -using Neo.IO.Caching; using Neo.IO.Json; using Neo.Network.P2P.Payloads; +using Neo.VM; +using Neo.VM.Types; +using System; using System.IO; using System.Linq; -namespace Neo.Ledger +namespace Neo.SmartContract.Native { - public class TrimmedBlock : BlockBase, ICloneable + public class TrimmedBlock : BlockBase, IInteroperable { public UInt256[] Hashes; public ConsensusData ConsensusData; - public bool IsBlock => Hashes.Length > 0; - - public Block GetBlock(DataCache cache) - { - return new Block - { - Version = Version, - PrevHash = PrevHash, - MerkleRoot = MerkleRoot, - Timestamp = Timestamp, - Index = Index, - NextConsensus = NextConsensus, - Witness = Witness, - ConsensusData = ConsensusData, - Transactions = Hashes.Skip(1).Select(p => cache[p].Transaction).ToArray() - }; - } - private Header _header = null; public Header Header { @@ -56,23 +40,6 @@ public Header Header + Hashes.GetVarSize() //Hashes + (ConsensusData?.Size ?? 0); //ConsensusData - TrimmedBlock ICloneable.Clone() - { - return new TrimmedBlock - { - Version = Version, - PrevHash = PrevHash, - MerkleRoot = MerkleRoot, - Timestamp = Timestamp, - Index = Index, - NextConsensus = NextConsensus, - Witness = Witness, - Hashes = Hashes, - ConsensusData = ConsensusData, - _header = _header - }; - } - public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); @@ -81,20 +48,6 @@ public override void Deserialize(BinaryReader reader) ConsensusData = reader.ReadSerializable(); } - void ICloneable.FromReplica(TrimmedBlock replica) - { - Version = replica.Version; - PrevHash = replica.PrevHash; - MerkleRoot = replica.MerkleRoot; - Timestamp = replica.Timestamp; - Index = replica.Index; - NextConsensus = replica.NextConsensus; - Witness = replica.Witness; - Hashes = replica.Hashes; - ConsensusData = replica.ConsensusData; - _header = replica._header; - } - public override void Serialize(BinaryWriter writer) { base.Serialize(writer); @@ -110,5 +63,30 @@ public override JObject ToJson() json["hashes"] = Hashes.Select(p => (JObject)p.ToString()).ToArray(); return json; } + + void IInteroperable.FromStackItem(StackItem stackItem) + { + throw new NotSupportedException(); + } + + StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) + { + return new VM.Types.Array(referenceCounter, new StackItem[] + { + // Computed properties + Hash.ToArray(), + + // BlockBase properties + Version, + PrevHash.ToArray(), + MerkleRoot.ToArray(), + Timestamp, + Index, + NextConsensus.ToArray(), + + // Block properties + Hashes.Length - 1 + }); + } } } diff --git a/src/neo/Ledger/StorageFlags.cs b/src/neo/SmartContract/StorageFlags.cs similarity index 81% rename from src/neo/Ledger/StorageFlags.cs rename to src/neo/SmartContract/StorageFlags.cs index cace3c97ef..5888426e66 100644 --- a/src/neo/Ledger/StorageFlags.cs +++ b/src/neo/SmartContract/StorageFlags.cs @@ -1,6 +1,6 @@ using System; -namespace Neo.Ledger +namespace Neo.SmartContract { [Flags] public enum StorageFlags : byte diff --git a/src/neo/Ledger/StorageItem.cs b/src/neo/SmartContract/StorageItem.cs similarity index 92% rename from src/neo/Ledger/StorageItem.cs rename to src/neo/SmartContract/StorageItem.cs index 7b8f99bec3..ff3d0d5bd1 100644 --- a/src/neo/Ledger/StorageItem.cs +++ b/src/neo/SmartContract/StorageItem.cs @@ -1,14 +1,13 @@ using Neo.IO; -using Neo.SmartContract; using Neo.VM; using System; using System.Collections.Generic; using System.IO; using System.Numerics; -namespace Neo.Ledger +namespace Neo.SmartContract { - public class StorageItem : ICloneable, ISerializable + public class StorageItem : ISerializable { private byte[] value; private object cache; @@ -61,7 +60,7 @@ public void Add(BigInteger integer) Set(this + integer); } - StorageItem ICloneable.Clone() + public StorageItem Clone() { return new StorageItem { @@ -76,7 +75,7 @@ public void Deserialize(BinaryReader reader) IsConstant = reader.ReadBoolean(); } - void ICloneable.FromReplica(StorageItem replica) + public void FromReplica(StorageItem replica) { Value = replica.Value; IsConstant = replica.IsConstant; diff --git a/src/neo/Ledger/StorageKey.cs b/src/neo/SmartContract/StorageKey.cs similarity index 98% rename from src/neo/Ledger/StorageKey.cs rename to src/neo/SmartContract/StorageKey.cs index 620f0d1fbd..99f4c0387c 100644 --- a/src/neo/Ledger/StorageKey.cs +++ b/src/neo/SmartContract/StorageKey.cs @@ -4,7 +4,7 @@ using System.Buffers.Binary; using System.IO; -namespace Neo.Ledger +namespace Neo.SmartContract { public class StorageKey : IEquatable, ISerializable { diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index 3be315f86d..7cbcbdc1a4 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -16,7 +16,7 @@ public class AssetDescriptor public AssetDescriptor(UInt160 asset_id) { - using SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); + using SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot(); var contract = NativeContract.ContractManagement.GetContract(snapshot, asset_id); if (contract is null) throw new ArgumentException(); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 141b21eec7..d5c9ef0a13 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -252,7 +252,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null { accounts = new[] { from }; } - using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) { Dictionary cosignerList = cosigners?.ToDictionary(p => p.Account) ?? new Dictionary(); byte[] script; @@ -323,14 +323,14 @@ public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Signer[ { accounts = new[] { sender }; } - using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) { var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); return MakeTransaction(snapshot, script, cosigners ?? Array.Empty(), attributes ?? Array.Empty(), balances_gas); } } - private Transaction MakeTransaction(StoreView snapshot, byte[] script, Signer[] cosigners, TransactionAttribute[] attributes, List<(UInt160 Account, BigInteger Value)> balances_gas) + private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] cosigners, TransactionAttribute[] attributes, List<(UInt160 Account, BigInteger Value)> balances_gas) { Random rand = new Random(); foreach (var (account, value) in balances_gas) @@ -340,13 +340,13 @@ private Transaction MakeTransaction(StoreView snapshot, byte[] script, Signer[] Version = 0, Nonce = (uint)rand.Next(), Script = script, - ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement, + ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + Transaction.MaxValidUntilBlockIncrement, Signers = GetSigners(account, cosigners), Attributes = attributes, }; // will try to execute 'transfer' script to check if it works - using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.Clone(), tx)) + using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.CreateSnapshot(), tx)) { if (engine.State == VMState.FAULT) { @@ -361,7 +361,7 @@ private Transaction MakeTransaction(StoreView snapshot, byte[] script, Signer[] throw new InvalidOperationException("Insufficient GAS"); } - public long CalculateNetworkFee(StoreView snapshot, Transaction tx) + public long CalculateNetworkFee(DataCache snapshot, Transaction tx) { UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); @@ -401,7 +401,7 @@ public long CalculateNetworkFee(StoreView snapshot, Transaction tx) size += Array.Empty().GetVarSize() * 2; // Check verify cost - using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.Clone()); + using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CreateSnapshot()); engine.LoadContract(contract, md, CallFlags.None); if (NativeContract.IsNative(hash)) engine.Push("verify"); if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index 2e2b64ba64..9faadd2ee4 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -10,7 +10,7 @@ namespace Neo.UnitTests.Extensions { public static class NativeContractExtensions { - public static ContractState DeployContract(this StoreView snapshot, UInt160 sender, byte[] nefFile, byte[] manifest, long gas = 200_00000000) + public static ContractState DeployContract(this DataCache snapshot, UInt160 sender, byte[] nefFile, byte[] manifest, long gas = 200_00000000) { var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nefFile, manifest, null); @@ -31,7 +31,7 @@ public static ContractState DeployContract(this StoreView snapshot, UInt160 send return ret; } - public static void UpdateContract(this StoreView snapshot, UInt160 callingScriptHash, byte[] nefFile, byte[] manifest) + public static void UpdateContract(this DataCache snapshot, UInt160 callingScriptHash, byte[] nefFile, byte[] manifest) { var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "update", nefFile, manifest, null); @@ -54,7 +54,7 @@ public static void UpdateContract(this StoreView snapshot, UInt160 callingScript } } - public static void DestroyContract(this StoreView snapshot, UInt160 callingScriptHash) + public static void DestroyContract(this DataCache snapshot, UInt160 callingScriptHash) { var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "destroy"); @@ -77,24 +77,24 @@ public static void DestroyContract(this StoreView snapshot, UInt160 callingScrip } } - public static void AddContract(this StoreView snapshot, UInt160 hash, ContractState state) + public static void AddContract(this DataCache snapshot, UInt160 hash, ContractState state) { var key = new KeyBuilder(NativeContract.ContractManagement.Id, 8).Add(hash); - snapshot.Storages.Add(key, new Neo.Ledger.StorageItem(state, false)); + snapshot.Add(key, new StorageItem(state, false)); } - public static void DeleteContract(this StoreView snapshot, UInt160 hash) + public static void DeleteContract(this DataCache snapshot, UInt160 hash) { var key = new KeyBuilder(NativeContract.ContractManagement.Id, 8).Add(hash); - snapshot.Storages.Delete(key); + snapshot.Delete(key); } - public static StackItem Call(this NativeContract contract, StoreView snapshot, string method, params ContractParameter[] args) + public static StackItem Call(this NativeContract contract, DataCache snapshot, string method, params ContractParameter[] args) { return Call(contract, snapshot, null, null, method, args); } - public static StackItem Call(this NativeContract contract, StoreView snapshot, IVerifiable container, Block persistingBlock, string method, params ContractParameter[] args) + public static StackItem Call(this NativeContract contract, DataCache snapshot, IVerifiable container, Block persistingBlock, string method, params ContractParameter[] args) { var engine = ApplicationEngine.Create(TriggerType.Application, container, snapshot, persistingBlock); var contractState = NativeContract.ContractManagement.GetContract(snapshot, contract.Hash); diff --git a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs index 6f5d548a04..4cba1e19e2 100644 --- a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs @@ -28,14 +28,14 @@ public void Deserialize(BinaryReader reader) { } public void DeserializeUnsigned(BinaryReader reader) { } - public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) => _hashForVerify; + public UInt160[] GetScriptHashesForVerifying(DataCache snapshot) => _hashForVerify; public void Serialize(BinaryWriter writer) { } public void SerializeUnsigned(BinaryWriter writer) { } } - public static bool Transfer(this NativeContract contract, StoreView snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom, Block persistingBlock) + public static bool Transfer(this NativeContract contract, DataCache snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom, Block persistingBlock) { var engine = ApplicationEngine.Create(TriggerType.Application, new ManualWitness(signFrom ? new UInt160(from) : null), snapshot, persistingBlock); @@ -61,7 +61,7 @@ public static bool Transfer(this NativeContract contract, StoreView snapshot, by return result.GetBoolean(); } - public static BigInteger TotalSupply(this NativeContract contract, StoreView snapshot) + public static BigInteger TotalSupply(this NativeContract contract, DataCache snapshot) { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); @@ -79,7 +79,7 @@ public static BigInteger TotalSupply(this NativeContract contract, StoreView sna return result.GetInteger(); } - public static BigInteger BalanceOf(this NativeContract contract, StoreView snapshot, byte[] account) + public static BigInteger BalanceOf(this NativeContract contract, DataCache snapshot, byte[] account) { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); @@ -98,7 +98,7 @@ public static BigInteger BalanceOf(this NativeContract contract, StoreView snaps return result.GetInteger(); } - public static BigInteger Decimals(this NativeContract contract, StoreView snapshot) + public static BigInteger Decimals(this NativeContract contract, DataCache snapshot) { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); @@ -116,7 +116,7 @@ public static BigInteger Decimals(this NativeContract contract, StoreView snapsh return result.GetInteger(); } - public static string Symbol(this NativeContract contract, StoreView snapshot) + public static string Symbol(this NativeContract contract, DataCache snapshot) { var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); diff --git a/tests/neo.UnitTests/IO/Caching/UT_CloneCache.cs b/tests/neo.UnitTests/IO/Caching/UT_CloneCache.cs index b75a70ee97..097ec526db 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_CloneCache.cs +++ b/tests/neo.UnitTests/IO/Caching/UT_CloneCache.cs @@ -1,39 +1,40 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.IO.Caching; +using Neo.Persistence; using System; using System.Collections.Generic; using System.Linq; +using System.Text; namespace Neo.UnitTests.IO.Caching { [TestClass] public class UT_CloneCache { - CloneCache cloneCache; - MyDataCache myDataCache; + ClonedCache clonedCache; + MyDataCache myDataCache; [TestInitialize] public void Init() { - myDataCache = new MyDataCache(); - cloneCache = new CloneCache(myDataCache); + myDataCache = new MyDataCache(); + clonedCache = new ClonedCache(myDataCache); } [TestMethod] public void TestCloneCache() { - cloneCache.Should().NotBeNull(); + clonedCache.Should().NotBeNull(); } [TestMethod] public void TestAddInternal() { - cloneCache.Add(new MyKey("key1"), new MyValue("value1")); - cloneCache[new MyKey("key1")].Should().Be(new MyValue("value1")); + clonedCache.Add(new MyKey("key1"), new MyValue("value1")); + clonedCache[new MyKey("key1")].Should().Be(new MyValue("value1")); - cloneCache.Commit(); + clonedCache.Commit(); myDataCache[new MyKey("key1")].Should().Be(new MyValue("value1")); } @@ -41,53 +42,53 @@ public void TestAddInternal() public void TestDeleteInternal() { myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - cloneCache.Delete(new MyKey("key1")); // trackable.State = TrackState.Deleted - cloneCache.Commit(); + clonedCache.Delete(new MyKey("key1")); // trackable.State = TrackState.Deleted + clonedCache.Commit(); - cloneCache.TryGet(new MyKey("key1")).Should().BeNull(); + clonedCache.TryGet(new MyKey("key1")).Should().BeNull(); myDataCache.TryGet(new MyKey("key1")).Should().BeNull(); } [TestMethod] public void TestFindInternal() { - cloneCache.Add(new MyKey("key1"), new MyValue("value1")); + clonedCache.Add(new MyKey("key1"), new MyValue("value1")); myDataCache.Add(new MyKey("key2"), new MyValue("value2")); myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - var items = cloneCache.Find(new MyKey("key1").ToArray()); + var items = clonedCache.Find(new MyKey("key1").ToArray()); items.ElementAt(0).Key.Should().Be(new MyKey("key1")); items.ElementAt(0).Value.Should().Be(new MyValue("value1")); items.Count().Should().Be(1); - items = cloneCache.Find(new MyKey("key2").ToArray()); + items = clonedCache.Find(new MyKey("key2").ToArray()); items.ElementAt(0).Key.Should().Be(new MyKey("key2")); - items.ElementAt(0).Value.Should().Be(new MyValue("value2")); + new MyValue("value2").Should().Be(items.ElementAt(0).Value); items.Count().Should().Be(1); - items = cloneCache.Find(new MyKey("key3").ToArray()); + items = clonedCache.Find(new MyKey("key3").ToArray()); items.ElementAt(0).Key.Should().Be(new MyKey("key3")); - items.ElementAt(0).Value.Should().Be(new MyValue("value3")); + new MyValue("value3").Should().Be(items.ElementAt(0).Value); items.Count().Should().Be(1); - items = cloneCache.Find(new MyKey("key4").ToArray()); + items = clonedCache.Find(new MyKey("key4").ToArray()); items.Count().Should().Be(0); } [TestMethod] public void TestGetInternal() { - cloneCache.Add(new MyKey("key1"), new MyValue("value1")); + clonedCache.Add(new MyKey("key1"), new MyValue("value1")); myDataCache.Add(new MyKey("key2"), new MyValue("value2")); myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - cloneCache[new MyKey("key1")].Should().Be(new MyValue("value1")); - cloneCache[new MyKey("key2")].Should().Be(new MyValue("value2")); - cloneCache[new MyKey("key3")].Should().Be(new MyValue("value3")); + new MyValue("value1").Should().Be(clonedCache[new MyKey("key1")]); + new MyValue("value2").Should().Be(clonedCache[new MyKey("key2")]); + new MyValue("value3").Should().Be(clonedCache[new MyKey("key3")]); Action action = () => { - var item = cloneCache[new MyKey("key4")]; + var item = clonedCache[new MyKey("key4")]; }; action.Should().Throw(); } @@ -95,33 +96,33 @@ public void TestGetInternal() [TestMethod] public void TestTryGetInternal() { - cloneCache.Add(new MyKey("key1"), new MyValue("value1")); + clonedCache.Add(new MyKey("key1"), new MyValue("value1")); myDataCache.Add(new MyKey("key2"), new MyValue("value2")); myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - cloneCache.TryGet(new MyKey("key1")).Should().Be(new MyValue("value1")); - cloneCache.TryGet(new MyKey("key2")).Should().Be(new MyValue("value2")); - cloneCache.TryGet(new MyKey("key3")).Should().Be(new MyValue("value3")); - cloneCache.TryGet(new MyKey("key4")).Should().BeNull(); + new MyValue("value1").Should().Be(clonedCache.TryGet(new MyKey("key1"))); + new MyValue("value2").Should().Be(clonedCache.TryGet(new MyKey("key2"))); + new MyValue("value3").Should().Be(clonedCache.TryGet(new MyKey("key3"))); + clonedCache.TryGet(new MyKey("key4")).Should().BeNull(); } [TestMethod] public void TestUpdateInternal() { - cloneCache.Add(new MyKey("key1"), new MyValue("value1")); + clonedCache.Add(new MyKey("key1"), new MyValue("value1")); myDataCache.Add(new MyKey("key2"), new MyValue("value2")); myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - cloneCache.GetAndChange(new MyKey("key1")).Value = "value_new_1"; - cloneCache.GetAndChange(new MyKey("key2")).Value = "value_new_2"; - cloneCache.GetAndChange(new MyKey("key3")).Value = "value_new_3"; + clonedCache.GetAndChange(new MyKey("key1")).Value = Encoding.Default.GetBytes("value_new_1"); + clonedCache.GetAndChange(new MyKey("key2")).Value = Encoding.Default.GetBytes("value_new_2"); + clonedCache.GetAndChange(new MyKey("key3")).Value = Encoding.Default.GetBytes("value_new_3"); - cloneCache.Commit(); + clonedCache.Commit(); - cloneCache[new MyKey("key1")].Should().Be(new MyValue("value_new_1")); - cloneCache[new MyKey("key2")].Should().Be(new MyValue("value_new_2")); - cloneCache[new MyKey("key3")].Should().Be(new MyValue("value_new_3")); - myDataCache[new MyKey("key2")].Should().Be(new MyValue("value_new_2")); + new MyValue("value_new_1").Should().Be(clonedCache[new MyKey("key1")]); + new MyValue("value_new_2").Should().Be(clonedCache[new MyKey("key2")]); + new MyValue("value_new_3").Should().Be(clonedCache[new MyKey("key3")]); + new MyValue("value_new_2").Should().Be(clonedCache[new MyKey("key2")]); } } } diff --git a/tests/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs b/tests/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs deleted file mode 100644 index 591ea8ea05..0000000000 --- a/tests/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs +++ /dev/null @@ -1,48 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Caching; - -namespace Neo.UnitTests.IO.Caching -{ - [TestClass] - public class UT_CloneMetaCache - { - MyMetaCache myMetaCache; - CloneMetaCache cloneMetaCache; - - [TestInitialize] - public void Init() - { - myMetaCache = new MyMetaCache(() => new MyValue()); - cloneMetaCache = new CloneMetaCache(myMetaCache); - } - - [TestMethod] - public void TestConstructor() - { - cloneMetaCache.Should().NotBeNull(); - } - - [TestMethod] - public void TestTryGetInternal() - { - MyValue value = myMetaCache.GetAndChange(); - value.Value = "value1"; - - cloneMetaCache.Get().Should().Be(value); - } - - [TestMethod] - public void TestUpdateInternal() - { - MyValue value = myMetaCache.GetAndChange(); - value.Value = "value1"; - - MyValue value2 = cloneMetaCache.GetAndChange(); - value2.Value = "value2"; - - cloneMetaCache.Commit(); - value.Value.Should().Be("value2"); - } - } -} diff --git a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs index b085ac2e82..7b36f5e38a 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs +++ b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs @@ -1,171 +1,192 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.IO.Caching; +using Neo.Persistence; +using Neo.SmartContract; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; namespace Neo.UnitTests.IO.Caching { - class MyKey : ISerializable, IEquatable, IComparable + class MyKey : StorageKey, IEquatable, IComparable, IComparable, IEquatable, IComparable { - public string Key; - public int Size => Key.Length; - public MyKey() { } + public MyKey(UInt256 hash) + { + Key = hash.ToArray(); + } + + public MyKey(StorageKey key) + { + Id = key.Id; + Key = key.Key; + } public MyKey(string val) { - Key = val; + Key = Encoding.UTF8.GetBytes(val); } public void Deserialize(BinaryReader reader) { - Key = reader.ReadString(); + Key = Encoding.UTF8.GetBytes(reader.ReadString()); } public void Serialize(BinaryWriter writer) { writer.Write(Key); } - public bool Equals(MyKey other) { - return Key.Equals(other.Key); + if (other == null) return false; + return Id == other.Id && Key.SequenceEqual(other.Key); } public override bool Equals(object obj) { - if (!(obj is MyKey key)) return false; - return Equals(key); + if (obj is not MyKey other) return false; + return Id == other.Id && Key.SequenceEqual(other.Key); } public override int GetHashCode() { - return Key.GetHashCode(); + return HashCode.Combine(Id, Key.Length); } public int CompareTo(MyKey obj) { - return Key.CompareTo(obj.Key); + return CompareTo((StorageKey)obj); } - } - - public class MyValue : ISerializable, ICloneable - { - public string Value; - public int Size => Value.Length; - - public MyValue() { } + public int CompareTo(StorageKey obj) + { + if (obj is null) throw new Exception(); + int ret = Id.CompareTo(obj.Id); + if (ret != 0) return ret; + return Encoding.UTF8.GetString(Key).CompareTo(Encoding.UTF8.GetString(obj.Key)); + } - public MyValue(string val) + public int CompareTo(object obj) { - Value = val; + if (obj is not StorageKey key) throw new Exception(); + return CompareTo(key); } + } - public void Deserialize(BinaryReader reader) + public class MyValue : StorageItem, ISerializable, IEquatable, IEquatable + { + public MyValue(UInt256 hash) { - Value = reader.ReadString(); + Value = hash.ToArray(); } - public void Serialize(BinaryWriter writer) + public MyValue(string val) { - writer.Write(Value); + Value = Encoding.Default.GetBytes(val); } - MyValue ICloneable.Clone() + public MyValue(byte[] val) { - return new MyValue(Value); + Value = val; } - void ICloneable.FromReplica(MyValue replica) + public void FromReplica(MyValue replica) { Value = replica.Value; } + public bool Equals(StorageItem other) + { + if (other == null) return false; + return (Value == null && other.Value == null) || Value.SequenceEqual(other.Value); + } + public bool Equals(MyValue other) { - return (Value == null && other.Value == null) || Value.Equals(other.Value); + if (other == null) return false; + return (Value == null && other.Value == null) || Value.SequenceEqual(other.Value); } public override bool Equals(object obj) { - if (!(obj is MyValue key)) return false; - return Equals(key); + if (obj is not StorageItem other) return false; + return (Value == null && other.Value == null) || Value.SequenceEqual(other.Value); } public override int GetHashCode() { - return Value.GetHashCode(); + return Value.Length; } } - class MyDataCache : DataCache - where TKey : IEquatable, ISerializable, new() - where TValue : class, ICloneable, ISerializable, new() + class MyDataCache : DataCache { - public Dictionary InnerDict = new Dictionary(); + public Dictionary InnerDict = new Dictionary(); - protected override void DeleteInternal(TKey key) + protected override void DeleteInternal(StorageKey key) { InnerDict.Remove(key); } - protected override void AddInternal(TKey key, TValue value) + protected override void AddInternal(StorageKey key, StorageItem value) { - InnerDict.Add(key, value); + InnerDict.Add(key, new MyValue(value.Value)); } - protected override IEnumerable<(TKey, TValue)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) + protected override IEnumerable<(StorageKey, StorageItem)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction = SeekDirection.Forward) { if (direction == SeekDirection.Forward) - return InnerDict.OrderBy(kvp => kvp.Key).Where(kvp => ByteArrayComparer.Default.Compare(kvp.Key.ToArray(), keyOrPrefix) >= 0).Select(p => (p.Key, p.Value)); + return InnerDict.OrderBy(kvp => kvp.Key) + .Where(kvp => ByteArrayComparer.Default.Compare(kvp.Key.ToArray(), keyOrPrefix) >= 0) + .Select(p => (p.Key, (StorageItem)new MyValue(p.Value.Value))); else - return InnerDict.OrderByDescending(kvp => kvp.Key).Where(kvp => ByteArrayComparer.Reverse.Compare(kvp.Key.ToArray(), keyOrPrefix) >= 0).Select(p => (p.Key, p.Value)); + return InnerDict.OrderByDescending(kvp => kvp.Key) + .Where(kvp => ByteArrayComparer.Reverse.Compare(kvp.Key.ToArray(), keyOrPrefix) >= 0) + .Select(p => (p.Key, (StorageItem)new MyValue(p.Value.Value))); } - protected override TValue GetInternal(TKey key) + protected override StorageItem GetInternal(StorageKey key) { - if (InnerDict.TryGetValue(key, out TValue value)) + if (InnerDict.TryGetValue(key, out var value)) { - return value.Clone(); + return new MyValue(value.Value); } throw new KeyNotFoundException(); } - protected override TValue TryGetInternal(TKey key) + protected override StorageItem TryGetInternal(StorageKey key) { - if (InnerDict.TryGetValue(key, out TValue value)) + if (InnerDict.TryGetValue(key, out var value)) { - return value.Clone(); + return new MyValue(value.Value); } return null; } - protected override bool ContainsInternal(TKey key) + protected override bool ContainsInternal(StorageKey key) { return InnerDict.ContainsKey(key); } - protected override void UpdateInternal(TKey key, TValue value) + protected override void UpdateInternal(StorageKey key, StorageItem value) { - InnerDict[key] = value; + InnerDict[key] = new MyValue(value.Value); } } [TestClass] public class UT_DataCache { - MyDataCache myDataCache; + MyDataCache myDataCache; [TestInitialize] public void Initialize() { - myDataCache = new MyDataCache(); + myDataCache = new MyDataCache(); } [TestMethod] @@ -269,8 +290,8 @@ public void TestFind() myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); var items = myDataCache.Find(new MyKey("key1").ToArray()); - items.ElementAt(0).Key.Should().Be(new MyKey("key1")); - items.ElementAt(0).Value.Should().Be(new MyValue("value1")); + new MyKey("key1").Should().Be(items.ElementAt(0).Key); + new MyValue("value1").Should().Be(items.ElementAt(0).Value); items.Count().Should().Be(1); items = myDataCache.Find(new MyKey("key5").ToArray()); @@ -287,14 +308,14 @@ public void TestSeek() myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); var items = myDataCache.Seek(new MyKey("key3").ToArray(), SeekDirection.Backward).ToArray(); - items[0].Key.Should().Be(new MyKey("key3")); - items[0].Value.Should().Be(new MyValue("value3")); - items[1].Key.Should().Be(new MyKey("key2")); - items[1].Value.Should().Be(new MyValue("value2")); - items.Count().Should().Be(3); + new MyKey("key3").Should().Be(items[0].Key); + new MyValue("value3").Should().Be(items[0].Value); + new MyKey("key2").Should().Be(items[1].Key); + new MyValue("value2").Should().Be(items[1].Value); + items.Length.Should().Be(3); items = myDataCache.Seek(new MyKey("key5").ToArray(), SeekDirection.Forward).ToArray(); - items.Count().Should().Be(0); + items.Length.Should().Be(0); } [TestMethod] @@ -307,15 +328,15 @@ public void TestFindRange() myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); var items = myDataCache.FindRange(new MyKey("key3").ToArray(), new MyKey("key5").ToArray()).ToArray(); - items[0].Key.Should().Be(new MyKey("key3")); - items[0].Value.Should().Be(new MyValue("value3")); - items[1].Key.Should().Be(new MyKey("key4")); - items[1].Value.Should().Be(new MyValue("value4")); - items.Count().Should().Be(2); + new MyKey("key3").Should().Be(items[0].Key); + new MyValue("value3").Should().Be(items[0].Value); + new MyKey("key4").Should().Be(items[1].Key); + new MyValue("value4").Should().Be(items[1].Value); + items.Length.Should().Be(2); // case 2 Need to sort the cache of myDataCache - myDataCache = new MyDataCache(); + myDataCache = new MyDataCache(); myDataCache.Add(new MyKey("key1"), new MyValue("value1")); myDataCache.Add(new MyKey("key2"), new MyValue("value2")); @@ -323,15 +344,15 @@ public void TestFindRange() myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); items = myDataCache.FindRange(new MyKey("key3").ToArray(), new MyKey("key5").ToArray()).ToArray(); - items[0].Key.Should().Be(new MyKey("key3")); - items[0].Value.Should().Be(new MyValue("value3")); - items[1].Key.Should().Be(new MyKey("key4")); - items[1].Value.Should().Be(new MyValue("value4")); - items.Count().Should().Be(2); + new MyKey("key3").Should().Be(items[0].Key); + new MyValue("value3").Should().Be(items[0].Value); + new MyKey("key4").Should().Be(items[1].Key); + new MyValue("value4").Should().Be(items[1].Value); + items.Length.Should().Be(2); // case 3 FindRange by Backward - myDataCache = new MyDataCache(); + myDataCache = new MyDataCache(); myDataCache.Add(new MyKey("key1"), new MyValue("value1")); myDataCache.Add(new MyKey("key2"), new MyValue("value2")); @@ -340,11 +361,11 @@ public void TestFindRange() myDataCache.InnerDict.Add(new MyKey("key5"), new MyValue("value5")); items = myDataCache.FindRange(new MyKey("key5").ToArray(), new MyKey("key3").ToArray(), SeekDirection.Backward).ToArray(); - items[0].Key.Should().Be(new MyKey("key5")); - items[0].Value.Should().Be(new MyValue("value5")); - items[1].Key.Should().Be(new MyKey("key4")); - items[1].Value.Should().Be(new MyValue("value4")); - items.Count().Should().Be(2); + new MyKey("key5").Should().Be(items[0].Key); + new MyValue("value5").Should().Be(items[0].Value); + new MyKey("key4").Should().Be(items[1].Key); + new MyValue("value4").Should().Be(items[1].Value); + items.Length.Should().Be(2); } [TestMethod] @@ -363,8 +384,8 @@ public void TestGetChangeSet() foreach (var item in items) { i++; - item.Key.Should().Be(new MyKey("key" + i)); - item.Item.Should().Be(new MyValue("value" + i)); + new MyKey("key" + i).Should().Be(item.Key); + new MyValue("value" + i).Should().Be(item.Item); } i.Should().Be(4); } @@ -413,7 +434,7 @@ public void TestTryGet() [TestMethod] public void TestFindInvalid() { - var myDataCache = new MyDataCache(); + var myDataCache = new MyDataCache(); myDataCache.Add(new MyKey("key1"), new MyValue("value1")); myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); diff --git a/tests/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs b/tests/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs deleted file mode 100644 index 1ed84a5a0e..0000000000 --- a/tests/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs +++ /dev/null @@ -1,76 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.IO.Caching; -using System; - -namespace Neo.UnitTests.IO.Caching -{ - public class MyMetaCache : MetaDataCache - where T : class, ICloneable, ISerializable, new() - { - public T Value; - - public MyMetaCache(Func factory) : base(factory) { } - - protected override void AddInternal(T item) - { - Value = item; - } - - protected override T TryGetInternal() - { - return Value; - } - - protected override void UpdateInternal(T item) - { - Value = item; - } - } - - [TestClass] - public class UT_MetaDataCache - { - MyMetaCache myMetaCache; - - [TestInitialize] - public void SetUp() - { - myMetaCache = new MyMetaCache(() => new MyValue()); - } - - [TestMethod] - public void TestContructor() - { - myMetaCache.Should().NotBeNull(); - } - - [TestMethod] - public void TestCommitAndAddInternal() - { - MyValue value = myMetaCache.Get(); - value.Should().NotBeNull(); - value.Value.Should().BeNull(); - - myMetaCache.Commit(); - myMetaCache.Value.Should().Be(value); - } - - public void TestCommitAndUpdateInternal() - { - MyValue value = myMetaCache.GetAndChange(); - value.Value = "value1"; - - myMetaCache.Commit(); - myMetaCache.Value.Should().Be(value); - myMetaCache.Value.Value.Should().Be("value1"); - } - - [TestMethod] - public void TestCreateSnapshot() - { - myMetaCache.CreateSnapshot().Should().NotBeNull(); - } - } -} diff --git a/tests/neo.UnitTests/IO/UT_SerializableWrapper.cs b/tests/neo.UnitTests/IO/UT_SerializableWrapper.cs deleted file mode 100644 index 0f73c40c38..0000000000 --- a/tests/neo.UnitTests/IO/UT_SerializableWrapper.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using System.IO; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_SerializableWrapper - { - [TestMethod] - public void TestGetSize() - { - SerializableWrapper temp = new SerializableWrapper(); - Assert.AreEqual(4, temp.Size); - } - - [TestMethod] - public void TestCast() - { - SerializableWrapper tempA = (SerializableWrapper)123; - SerializableWrapper tempB = tempA.ToArray().AsSerializable>(); - - Assert.IsTrue(tempA.Equals(tempB)); - Assert.AreEqual((uint)123, (uint)tempA); - } - - [TestMethod] - public void TestEqualsOtherObject() - { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - BinaryReader reader = new BinaryReader(stream); - writer.Write((uint)1); - stream.Seek(0, SeekOrigin.Begin); - SerializableWrapper temp = new SerializableWrapper(); - temp.Deserialize(reader); - Assert.AreEqual(true, temp.Equals(1u)); - } - - [TestMethod] - public void TestEqualsOtherSerializableWrapper() - { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - BinaryReader reader = new BinaryReader(stream); - writer.Write((uint)1); - stream.Seek(0, SeekOrigin.Begin); - SerializableWrapper temp = new SerializableWrapper(); - temp.Deserialize(reader); - MemoryStream stream2 = new MemoryStream(); - BinaryWriter writer2 = new BinaryWriter(stream2); - BinaryReader reader2 = new BinaryReader(stream2); - writer2.Write((uint)1); - stream2.Seek(0, SeekOrigin.Begin); - SerializableWrapper temp2 = new SerializableWrapper(); - temp2.Deserialize(reader2); - Assert.AreEqual(true, temp.Equals(temp2)); - } - } -} diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index b0c12dc72e..c2051f40d4 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -7,7 +7,6 @@ using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; -using Neo.VM; using Neo.Wallets; using Neo.Wallets.NEP6; using System; @@ -18,7 +17,7 @@ namespace Neo.UnitTests.Ledger { internal class TestBlock : Block { - public override bool Verify(StoreView snapshot) + public override bool Verify(DataCache snapshot) { return true; } @@ -31,7 +30,7 @@ public static TestBlock Cast(Block input) internal class TestHeader : Header { - public override bool Verify(StoreView snapshot) + public override bool Verify(DataCache snapshot) { return true; } @@ -62,51 +61,12 @@ public void Initialize() Blockchain.Singleton.MemPool.TryAdd(txSample, Blockchain.Singleton.GetSnapshot()); } - [TestMethod] - public void TestContainsBlock() - { - Blockchain.Singleton.ContainsBlock(UInt256.Zero).Should().BeFalse(); - } - - [TestMethod] - public void TestContainsTransaction() - { - Blockchain.Singleton.ContainsTransaction(UInt256.Zero).Should().BeFalse(); - Blockchain.Singleton.ContainsTransaction(txSample.Hash).Should().BeFalse(); - } - [TestMethod] public void TestGetCurrentBlockHash() { Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0x00c6803707b564153d444bfcdf3a13325fc96dda55cc8a740bbd543a1d752fda")); } - [TestMethod] - public void TestGetCurrentHeaderHash() - { - Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0x00c6803707b564153d444bfcdf3a13325fc96dda55cc8a740bbd543a1d752fda")); - } - - [TestMethod] - public void TestGetBlock() - { - Blockchain.Singleton.GetBlock(UInt256.Zero).Should().BeNull(); - } - - [TestMethod] - public void TestGetBlockHash() - { - Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0x00c6803707b564153d444bfcdf3a13325fc96dda55cc8a740bbd543a1d752fda")); - Blockchain.Singleton.GetBlockHash(10).Should().BeNull(); - } - - [TestMethod] - public void TestGetTransaction() - { - Blockchain.Singleton.GetTransaction(UInt256.Zero).Should().BeNull(); - Blockchain.Singleton.GetTransaction(txSample.Hash).Should().BeNull(); - } - [TestMethod] public void TestValidTransaction() { @@ -120,7 +80,7 @@ public void TestValidTransaction() // Fake balance var key = new KeyBuilder(NativeContract.GAS.Id, 20).Add(acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -139,34 +99,6 @@ public void TestValidTransaction() senderProbe.ExpectMsg(p => p.Result == VerifyResult.AlreadyExists); } - [TestMethod] - public void TestInvalidTransactionInPersist() - { - var snapshot = Blockchain.Singleton.GetSnapshot(); - var tx = new Transaction() - { - Attributes = Array.Empty(), - Signers = Array.Empty(), - NetworkFee = 0, - Nonce = (uint)Environment.TickCount, - Script = new byte[] { 1 }, - SystemFee = 0, - ValidUntilBlock = Blockchain.GenesisBlock.Index + 1, - Version = 0, - Witnesses = new Witness[0], - }; - StoreView clonedSnapshot = snapshot.Clone(); - var state = new TransactionState - { - BlockIndex = 0, - Transaction = tx - }; - clonedSnapshot.Transactions.Add(tx.Hash, state); - clonedSnapshot.Transactions.Commit(); - state.VMState = VMState.FAULT; - snapshot.Transactions.TryGet(tx.Hash).VMState.Should().Be(VMState.FAULT); - } - internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) { StorageKey storageKey = new StorageKey diff --git a/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs b/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs index b7a0de93c8..b47b44e224 100644 --- a/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs +++ b/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs @@ -1,7 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.Ledger; +using Neo.SmartContract; +using Neo.SmartContract.Native; using System.IO; namespace Neo.UnitTests.Ledger @@ -22,42 +22,20 @@ public void Initialize() } [TestMethod] - public void TestClone() + public void TestDeserialize() { - HashIndexState dest = ((ICloneable)origin).Clone(); - dest.Hash.Should().Be(origin.Hash); - dest.Index.Should().Be(origin.Index); - } + using MemoryStream ms = new MemoryStream(1024); + using BinaryReader reader = new BinaryReader(ms); + + var data = BinarySerializer.Serialize(((IInteroperable)origin).ToStackItem(null), 1024); + ms.Write(data); + ms.Seek(0, SeekOrigin.Begin); - [TestMethod] - public void TestFromReplica() - { HashIndexState dest = new HashIndexState(); - ((ICloneable)dest).FromReplica(origin); + ((IInteroperable)dest).FromStackItem(BinarySerializer.Deserialize(reader, 1024, 1024, null)); + dest.Hash.Should().Be(origin.Hash); dest.Index.Should().Be(origin.Index); } - - [TestMethod] - public void TestGetSize() - { - ((ISerializable)origin).Size.Should().Be(36); - } - - [TestMethod] - public void TestDeserialize() - { - using (MemoryStream ms = new MemoryStream(1024)) - using (BinaryWriter writer = new BinaryWriter(ms)) - using (BinaryReader reader = new BinaryReader(ms)) - { - ((ISerializable)origin).Serialize(writer); - ms.Seek(0, SeekOrigin.Begin); - HashIndexState dest = new HashIndexState(); - ((ISerializable)dest).Deserialize(reader); - dest.Hash.Should().Be(origin.Hash); - dest.Index.Should().Be(origin.Index); - } - } } } diff --git a/tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs b/tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs deleted file mode 100644 index 482c620a00..0000000000 --- a/tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs +++ /dev/null @@ -1,59 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.Ledger; -using System.IO; - -namespace Neo.UnitTests.Ledger -{ - [TestClass] - public class UT_HeaderHashList - { - HeaderHashList origin; - - [TestInitialize] - public void Initialize() - { - origin = new HeaderHashList - { - Hashes = new UInt256[] { UInt256.Zero } - }; - } - - [TestMethod] - public void TestClone() - { - HeaderHashList dest = ((ICloneable)origin).Clone(); - dest.Hashes.Should().BeEquivalentTo(origin.Hashes); - } - - [TestMethod] - public void TestFromReplica() - { - HeaderHashList dest = new HeaderHashList(); - ((ICloneable)dest).FromReplica(origin); - dest.Hashes.Should().BeEquivalentTo(origin.Hashes); - } - - [TestMethod] - public void TestDeserialize() - { - using (MemoryStream ms = new MemoryStream(1024)) - using (BinaryWriter writer = new BinaryWriter(ms)) - using (BinaryReader reader = new BinaryReader(ms)) - { - ((ISerializable)origin).Serialize(writer); - ms.Seek(0, SeekOrigin.Begin); - HeaderHashList dest = new HeaderHashList(); - ((ISerializable)dest).Deserialize(reader); - dest.Hashes.Should().BeEquivalentTo(origin.Hashes); - } - } - - [TestMethod] - public void TestGetSize() - { - ((ISerializable)origin).Size.Should().Be(33); - } - } -} diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 7edab47e07..8e8f31841c 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -83,8 +83,8 @@ private Transaction CreateTransactionWithFee(long fee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); - mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); mock.Setup(p => p.VerifyStateIndependent()).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.NetworkFee = fee; @@ -108,8 +108,8 @@ private Transaction CreateTransactionWithFeeAndBalanceVerify(long fee) random.NextBytes(randomBytes); Mock mock = new Mock(); UInt160 sender = senderAccount; - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); - mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny())).Returns((StoreView snapshot, TransactionVerificationContext context) => context.CheckTransaction(mock.Object, snapshot) ? VerifyResult.Succeed : VerifyResult.InsufficientFunds); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny())).Returns((DataCache snapshot, TransactionVerificationContext context) => context.CheckTransaction(mock.Object, snapshot) ? VerifyResult.Succeed : VerifyResult.InsufficientFunds); mock.Setup(p => p.VerifyStateIndependent()).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.NetworkFee = fee; @@ -151,7 +151,7 @@ private void AddTransaction(Transaction txToAdd) _unit.TryAdd(txToAdd, snapshot); } - private void AddTransactionsWithBalanceVerify(int count, long fee, SnapshotView snapshot) + private void AddTransactionsWithBalanceVerify(int count, long fee, DataCache snapshot) { for (int i = 0; i < count; i++) { @@ -221,7 +221,7 @@ public void BlockPersistMovesTxToUnverifiedAndReverification() [TestMethod] public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() { - SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); + using SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot(); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, senderAccount); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); @@ -506,8 +506,8 @@ public void TestUpdatePoolForBlockPersisted() var key2 = CreateStorageKey(Prefix_FeePerByte); key1.Id = NativeContract.Policy.Id; key2.Id = NativeContract.Policy.Id; - snapshot.Storages.Add(key1, item1); - snapshot.Storages.Add(key2, item2); + snapshot.Add(key1, item1); + snapshot.Add(key2, item2); var tx1 = CreateTransaction(); var tx2 = CreateTransaction(); diff --git a/tests/neo.UnitTests/Ledger/UT_StorageItem.cs b/tests/neo.UnitTests/Ledger/UT_StorageItem.cs index 5ed24b5858..f9ced186ec 100644 --- a/tests/neo.UnitTests/Ledger/UT_StorageItem.cs +++ b/tests/neo.UnitTests/Ledger/UT_StorageItem.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.Ledger; +using Neo.SmartContract; using System.IO; using System.Text; @@ -53,7 +52,7 @@ public void Clone() { uut.Value = TestUtils.GetByteArray(10, 0x42); - StorageItem newSi = ((ICloneable)uut).Clone(); + StorageItem newSi = uut.Clone(); newSi.Value.Length.Should().Be(10); newSi.Value[0].Should().Be(0x42); for (int i = 1; i < 10; i++) @@ -112,7 +111,7 @@ public void TestFromReplica() uut.Value = TestUtils.GetByteArray(10, 0x42); uut.IsConstant = true; StorageItem dest = new StorageItem(); - ((ICloneable)dest).FromReplica(uut); + dest.FromReplica(uut); dest.Value.Should().BeEquivalentTo(uut.Value); dest.IsConstant.Should().Be(uut.IsConstant); } diff --git a/tests/neo.UnitTests/Ledger/UT_StorageKey.cs b/tests/neo.UnitTests/Ledger/UT_StorageKey.cs index e71cdba716..1aa347eb5e 100644 --- a/tests/neo.UnitTests/Ledger/UT_StorageKey.cs +++ b/tests/neo.UnitTests/Ledger/UT_StorageKey.cs @@ -1,7 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.Ledger; +using Neo.SmartContract; using System.IO; namespace Neo.UnitTests.Ledger diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs index cfb3f84d2d..6d06e582a1 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs +++ b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs @@ -1,8 +1,8 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; using System; using System.IO; @@ -19,8 +19,7 @@ public void Initialize() origin = new TransactionState { BlockIndex = 1, - VMState = VM.VMState.NONE, - Transaction = new Neo.Network.P2P.Payloads.Transaction() + Transaction = new Transaction() { Attributes = Array.Empty(), Script = new byte[] { 0x01 }, @@ -34,45 +33,20 @@ public void Initialize() } [TestMethod] - public void TestClone() + public void TestDeserialize() { - TransactionState dest = ((ICloneable)origin).Clone(); - dest.BlockIndex.Should().Be(origin.BlockIndex); - dest.VMState.Should().Be(origin.VMState); - dest.Transaction.Should().Be(origin.Transaction); - } + using MemoryStream ms = new MemoryStream(1024); + using BinaryReader reader = new BinaryReader(ms); - [TestMethod] - public void TestFromReplica() - { - TransactionState dest = new TransactionState(); - ((ICloneable)dest).FromReplica(origin); - dest.BlockIndex.Should().Be(origin.BlockIndex); - dest.VMState.Should().Be(origin.VMState); - dest.Transaction.Should().Be(origin.Transaction); - } + var data = BinarySerializer.Serialize(((IInteroperable)origin).ToStackItem(null), 1024); + ms.Write(data); + ms.Seek(0, SeekOrigin.Begin); - [TestMethod] - public void TestDeserialize() - { - using (MemoryStream ms = new MemoryStream(1024)) - using (BinaryWriter writer = new BinaryWriter(ms)) - using (BinaryReader reader = new BinaryReader(ms)) - { - ((ISerializable)origin).Serialize(writer); - ms.Seek(0, SeekOrigin.Begin); - TransactionState dest = new TransactionState(); - ((ISerializable)dest).Deserialize(reader); - dest.BlockIndex.Should().Be(origin.BlockIndex); - dest.VMState.Should().Be(origin.VMState); - dest.Transaction.Hash.Should().Be(origin.Transaction.Hash); - } - } + TransactionState dest = new TransactionState(); + ((IInteroperable)dest).FromStackItem(BinarySerializer.Deserialize(reader, 1024, 1024, null)); - [TestMethod] - public void TestGetSize() - { - ((ISerializable)origin).Size.Should().Be(58); + dest.BlockIndex.Should().Be(origin.BlockIndex); + dest.Transaction.Hash.Should().Be(origin.Transaction.Hash); } } } diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs index 2de4f9084a..291d9b7d1a 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs +++ b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs @@ -28,8 +28,8 @@ private Transaction CreateTransactionWithFee(long networkFee, long systemFee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); - mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); mock.Setup(p => p.VerifyStateIndependent()).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.NetworkFee = networkFee; diff --git a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs index c8b0e30fb8..c27413f1d3 100644 --- a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs +++ b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -1,8 +1,9 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.SmartContract.Native; +using Neo.UnitTests.SmartContract; using Neo.VM; using System; using System.IO; @@ -24,19 +25,17 @@ public static TrimmedBlock GetTrimmedBlockWithNoTransaction() NextConsensus = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), Witness = new Witness { - InvocationScript = new byte[0], + InvocationScript = Array.Empty(), VerificationScript = new[] { (byte)OpCode.PUSH1 } }, - Hashes = new UInt256[0] + Hashes = Array.Empty() }; } - [TestMethod] - public void TestGetIsBlock() + [TestInitialize] + public void Init() { - TrimmedBlock block = GetTrimmedBlockWithNoTransaction(); - block.Hashes = new UInt256[] { TestUtils.GetTransaction(UInt160.Zero).Hash }; - block.IsBlock.Should().BeTrue(); + TestBlockchain.InitializeMockNeoSystem(); } [TestMethod] @@ -63,17 +62,20 @@ public void TestGetBlock() Transaction = tx2, BlockIndex = 1 }; - snapshot.Transactions.Add(tx1.Hash, state1); - snapshot.Transactions.Add(tx2.Hash, state2); + UT_SmartContractHelper.TransactionAdd(snapshot, state1, state2); TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); tblock.Hashes = new UInt256[] { tx1.Hash, tx2.Hash }; - Block block = tblock.GetBlock(snapshot.Transactions); + UT_SmartContractHelper.BlocksAdd(snapshot, tblock.Hash, tblock); + + Block block = NativeContract.Ledger.GetBlock(snapshot, tblock.Hash); block.Index.Should().Be(1); block.MerkleRoot.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02")); block.Transactions.Length.Should().Be(1); block.Transactions[0].Hash.Should().Be(tx2.Hash); + block.Witness.InvocationScript.ToHexString().Should().Be(tblock.Witness.InvocationScript.ToHexString()); + block.Witness.VerificationScript.ToHexString().Should().Be(tblock.Witness.VerificationScript.ToHexString()); } [TestMethod] @@ -114,25 +116,5 @@ public void TestDeserialize() tblock.Witness.ScriptHash.Should().Be(newBlock.Witness.ScriptHash); tblock.ToJson().ToString().Should().Be(newBlock.ToJson().ToString()); } - - [TestMethod] - public void TestClone() - { - TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); - tblock.Hashes = new UInt256[] { TestUtils.GetTransaction(UInt160.Zero).Hash }; - ICloneable cloneable = tblock; - var clonedBlock = cloneable.Clone(); - clonedBlock.ToJson().ToString().Should().Be(tblock.ToJson().ToString()); - } - - [TestMethod] - public void TestFromReplica() - { - TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); - tblock.Hashes = new UInt256[] { TestUtils.GetTransaction(UInt160.Zero).Hash }; - ICloneable cloneable = new TrimmedBlock(); - cloneable.FromReplica(tblock); - ((TrimmedBlock)cloneable).ToJson().ToString().Should().Be(tblock.ToJson().ToString()); - } } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index 0d5208de5a..533f277f9a 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -135,13 +135,7 @@ public void Equals_DiffObj() Block newBlock = new Block(); UInt256 val256 = UInt256.Zero; UInt256 prevHash = new UInt256(TestUtils.GetByteArray(32, 0x42)); - UInt256 merkRoot; - UInt160 val160; - ulong timestampVal; - uint indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(newBlock, val256, out merkRoot, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 1); + TestUtils.SetupBlockWithValues(newBlock, val256, out UInt256 merkRoot, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, 1); TestUtils.SetupBlockWithValues(uut, prevHash, out merkRoot, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 0); uut.Equals(newBlock).Should().BeFalse(); @@ -156,16 +150,9 @@ public void Equals_Null() [TestMethod] public void Equals_SameHash() { - Block newBlock = new Block(); UInt256 prevHash = new UInt256(TestUtils.GetByteArray(32, 0x42)); - UInt256 merkRoot; - UInt160 val160; - ulong timestampVal; - uint indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(newBlock, prevHash, out merkRoot, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 1); + TestUtils.SetupBlockWithValues(newBlock, prevHash, out UInt256 merkRoot, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, 1); TestUtils.SetupBlockWithValues(uut, prevHash, out merkRoot, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 1); uut.Equals(newBlock).Should().BeTrue(); @@ -175,19 +162,11 @@ public void Equals_SameHash() public void RebuildMerkleRoot_Updates() { UInt256 val256 = UInt256.Zero; - UInt256 merkRoot; - UInt160 val160; - ulong timestampVal; - uint indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRoot, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 1); - + TestUtils.SetupBlockWithValues(uut, val256, out UInt256 merkRoot, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, 1); UInt256 merkleRoot = uut.MerkleRoot; TestUtils.SetupBlockWithValues(uut, val256, out merkRoot, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 3); uut.RebuildMerkleRoot(); - uut.MerkleRoot.Should().NotBe(merkleRoot); } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index afec8fd41c..fcc9c3e1ee 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -1,7 +1,10 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; +using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.SmartContract.Native; +using Neo.UnitTests.SmartContract; using System.IO; namespace Neo.UnitTests.Network.P2P.Payloads @@ -14,6 +17,7 @@ public class UT_Header [TestInitialize] public void TestSetup() { + TestBlockchain.InitializeMockNeoSystem(); uut = new Header(); } @@ -39,8 +43,22 @@ public void GetHashCodeTest() public void TrimTest() { UInt256 val256 = UInt256.Zero; + var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); TestUtils.SetupHeaderWithValues(uut, val256, out _, out _, out _, out _, out _); - var trim = uut.Trim(); + uut.Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] }; + + UT_SmartContractHelper.BlocksAdd(snapshot, uut.Hash, new TrimmedBlock() + { + Timestamp = uut.Timestamp, + PrevHash = uut.PrevHash, + MerkleRoot = uut.MerkleRoot, + ConsensusData = new ConsensusData(), + Hashes = new UInt256[0], + NextConsensus = uut.NextConsensus, + Witness = uut.Witness + }); + + var trim = NativeContract.Ledger.GetTrimmedBlock(snapshot, uut.Hash); trim.Version.Should().Be(uut.Version); trim.PrevHash.Should().Be(uut.PrevHash); @@ -48,7 +66,7 @@ public void TrimTest() trim.Timestamp.Should().Be(uut.Timestamp); trim.Index.Should().Be(uut.Index); trim.NextConsensus.Should().Be(uut.NextConsensus); - trim.Witness.Should().Be(uut.Witness); + trim.Witness.Should().BeEquivalentTo(uut.Witness); trim.Hashes.Length.Should().Be(0); } @@ -64,16 +82,14 @@ public void Deserialize() using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) { - using (BinaryReader reader = new BinaryReader(ms)) - { - uut.Deserialize(reader); - } + using BinaryReader reader = new BinaryReader(ms); + uut.Deserialize(reader); } - assertStandardHeaderTestVals(val256, merkRoot, val160, timestampVal, indexVal, scriptVal); + AssertStandardHeaderTestVals(val256, merkRoot, val160, timestampVal, indexVal, scriptVal); } - private void assertStandardHeaderTestVals(UInt256 val256, UInt256 merkRoot, UInt160 val160, ulong timestampVal, uint indexVal, Witness scriptVal) + private void AssertStandardHeaderTestVals(UInt256 val256, UInt256 merkRoot, UInt160 val160, ulong timestampVal, uint indexVal, Witness scriptVal) { uut.PrevHash.Should().Be(val256); uut.MerkleRoot.Should().Be(merkRoot); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index b4f0934307..bd949063d3 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -125,7 +125,7 @@ public void FeeIsMultiSigContract() // Fake balance var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -195,7 +195,7 @@ public void FeeIsSignatureContractDetailed() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -305,7 +305,7 @@ public void FeeIsSignatureContract_TestScope_Global() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -391,7 +391,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -478,7 +478,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -568,7 +568,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -618,7 +618,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -710,7 +710,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -971,7 +971,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -1114,7 +1114,7 @@ public void Test_VerifyStateIndependent() // Fake balance var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -1148,6 +1148,7 @@ public void Test_VerifyStateIndependent() public void Test_VerifyStateDependent() { var snapshot = Blockchain.Singleton.GetSnapshot(); + var height = NativeContract.Ledger.CurrentIndex(snapshot); var tx = new Transaction() { Attributes = Array.Empty(), @@ -1156,7 +1157,7 @@ public void Test_VerifyStateDependent() Script = new byte[0], Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, SystemFee = 0, - ValidUntilBlock = snapshot.Height + 1, + ValidUntilBlock = height + 1, Version = 0, Witnesses = new Witness[0], }; @@ -1186,7 +1187,7 @@ public void Test_VerifyStateDependent() // Fake balance var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -1256,7 +1257,7 @@ public void Test_Verify() // Fake balance var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; diff --git a/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs b/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs index 101ed0bdfc..5feff2b677 100644 --- a/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs +++ b/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs @@ -13,17 +13,17 @@ public void StoreTest() { using var store = new MemoryStore(); - store.Delete(0, new byte[] { 1 }); - Assert.AreEqual(null, store.TryGet(1, new byte[] { 1 })); - store.Put(1, new byte[] { 1 }, new byte[] { 1, 2, 3 }); - CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, store.TryGet(1, new byte[] { 1 })); + store.Delete(new byte[] { 1 }); + Assert.AreEqual(null, store.TryGet(new byte[] { 1 })); + store.Put(new byte[] { 1 }, new byte[] { 1, 2, 3 }); + CollectionAssert.AreEqual(new byte[] { 1, 2, 3 }, store.TryGet(new byte[] { 1 })); - store.Put(1, new byte[] { 2 }, new byte[] { 4, 5, 6 }); - CollectionAssert.AreEqual(new byte[] { 1 }, store.Seek(1, new byte[] { }, SeekDirection.Forward).Select(u => u.Key).First()); - CollectionAssert.AreEqual(new byte[] { 2 }, store.Seek(1, new byte[] { }, SeekDirection.Backward).Select(u => u.Key).First()); - CollectionAssert.AreEqual(new byte[] { 1 }, store.Seek(1, new byte[] { 1 }, SeekDirection.Backward).Select(u => u.Key).First()); + store.Put(new byte[] { 2 }, new byte[] { 4, 5, 6 }); + CollectionAssert.AreEqual(new byte[] { 1 }, store.Seek(new byte[] { }, SeekDirection.Forward).Select(u => u.Key).First()); + CollectionAssert.AreEqual(new byte[] { 2 }, store.Seek(new byte[] { }, SeekDirection.Backward).Select(u => u.Key).First()); + CollectionAssert.AreEqual(new byte[] { 1 }, store.Seek(new byte[] { 1 }, SeekDirection.Backward).Select(u => u.Key).First()); - store.Delete(0, new byte[] { 1 }); + store.Delete(new byte[] { 1 }); } } } diff --git a/tests/neo.UnitTests/Persistence/UT_ReadOnlyView.cs b/tests/neo.UnitTests/Persistence/UT_ReadOnlyView.cs deleted file mode 100644 index 4e7921cb00..0000000000 --- a/tests/neo.UnitTests/Persistence/UT_ReadOnlyView.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Persistence; -using System; -using System.Linq; - -namespace Neo.UnitTests.Persistence -{ - [TestClass] - public class UT_ReadOnlyView - { - [TestMethod] - public void CommitException() - { - var r = new ReadOnlyView(new MemoryStore()); - Assert.ThrowsException(() => r.Commit()); - } - - [TestMethod] - public void Stores() - { - var r = new ReadOnlyView(new MemoryStore()); - - Assert.AreEqual(uint.MaxValue, r.BlockHashIndex.Get().Index); - Assert.AreEqual(UInt256.Zero, r.BlockHashIndex.Get().Hash); - Assert.AreEqual(uint.MaxValue, r.HeaderHashIndex.Get().Index); - Assert.AreEqual(UInt256.Zero, r.HeaderHashIndex.Get().Hash); - Assert.AreEqual(0, r.Blocks.Find().Count()); - Assert.AreEqual(0, r.Transactions.Find().Count()); - Assert.AreEqual(0, r.Storages.Find().Count()); - Assert.AreEqual(0, r.HeaderHashList.Find().Count()); - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs index 8018dcaa16..fdf93436f4 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs @@ -2,6 +2,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; +using Neo.SmartContract; using Neo.SmartContract.Native; using System; using System.Numerics; @@ -39,7 +40,7 @@ public void TestTotalSupply() key.Id = test.Id; - snapshot.Storages.Add(key, item); + snapshot.Add(key, item); test.TotalSupply(snapshot).Should().Be(1); } @@ -59,7 +60,7 @@ public void TestTotalSupplyDecimal() key.Id = test.Id; - snapshot.Storages.Add(key, item); + snapshot.Add(key, item); test.TotalSupply(snapshot).Should().Be(10_000_000_000_000_000); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs index 7bcfe23bdd..ada89b0973 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -17,7 +17,7 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_GasToken { - private StoreView _snapshot; + private DataCache _snapshot; private Block _persistingBlock; [TestInitialize] @@ -41,11 +41,11 @@ public void TestSetup() [TestMethod] public void Check_BalanceOfTransferAndBurn() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000 }; - byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); byte[] to = new byte[20]; - var keyCount = snapshot.Storages.GetChangeSet().Count(); + var keyCount = snapshot.GetChangeSet().Count(); var supply = NativeContract.GAS.TotalSupply(snapshot); supply.Should().Be(3000000050000000); // 3000000000000000 + 50000000 (neo holder reward) @@ -73,11 +73,11 @@ public void Check_BalanceOfTransferAndBurn() supply = NativeContract.GAS.TotalSupply(snapshot); supply.Should().Be(3000050050000000); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 3); // Gas + snapshot.GetChangeSet().Count().Should().Be(keyCount + 3); // Gas // Transfer - keyCount = snapshot.Storages.GetChangeSet().Count(); + keyCount = snapshot.GetChangeSet().Count(); NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000000, false, persistingBlock).Should().BeFalse(); // Not signed NativeContract.GAS.Transfer(snapshot, from, to, 30000500_00000001, true, persistingBlock).Should().BeFalse(); // More than balance @@ -88,12 +88,12 @@ public void Check_BalanceOfTransferAndBurn() NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(30000500_00000000); NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(0); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 1); // All + snapshot.GetChangeSet().Count().Should().Be(keyCount + 1); // All // Burn using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, 0); - keyCount = snapshot.Storages.GetChangeSet().Count(); + keyCount = snapshot.GetChangeSet().Count(); Assert.ThrowsException(() => NativeContract.GAS.Burn(engine, new UInt160(to), BigInteger.MinusOne)); @@ -109,13 +109,13 @@ public void Check_BalanceOfTransferAndBurn() NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000049999999999); - keyCount.Should().Be(snapshot.Storages.GetChangeSet().Count()); + keyCount.Should().Be(snapshot.GetChangeSet().Count()); // Burn all NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000049999999999)); - (keyCount - 1).Should().Be(snapshot.Storages.GetChangeSet().Count()); + (keyCount - 1).Should().Be(snapshot.GetChangeSet().Count()); // Bad inputs diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs index 507bd4e90c..8e87e36c4c 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs @@ -18,7 +18,7 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_NameService : TestKit { - protected StoreView _snapshot; + protected DataCache _snapshot; [TestInitialize] public void TestSetup() @@ -37,7 +37,7 @@ public void TestInfo() [TestMethod] public void TestRoots() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000 }; var from = NativeContract.NEO.GetCommitteeAddress(snapshot); @@ -62,7 +62,7 @@ public void TestRoots() [TestMethod] public void TestPrice() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000 }; var from = NativeContract.NEO.GetCommitteeAddress(snapshot); @@ -87,7 +87,7 @@ public void TestPrice() [TestMethod] public void TestRegister() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000, Timestamp = 0 }; var from = NativeContract.NEO.GetCommitteeAddress(snapshot); @@ -126,7 +126,7 @@ public void TestRegister() [TestMethod] public void TestSetRecord() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000, Timestamp = 0 }; var from = NativeContract.NEO.GetCommitteeAddress(snapshot); @@ -158,7 +158,7 @@ public void TestSetRecord() CollectionAssert.AreEqual(System.Array.Empty(), NativeContract.NameService.GetRecords(snapshot, "neo.com").Select(u => u.Type.ToString() + "=" + u.Data).ToArray()); } - internal static bool Check_DeleteRecord(StoreView snapshot, string name, RecordType type, UInt160 signedBy, Block persistingBlock) + internal static bool Check_DeleteRecord(DataCache snapshot, string name, RecordType type, UInt160 signedBy, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); using var script = new ScriptBuilder(); @@ -176,7 +176,7 @@ internal static bool Check_DeleteRecord(StoreView snapshot, string name, RecordT return true; } - internal static bool Check_SetRecord(StoreView snapshot, string name, RecordType type, string data, UInt160 signedBy, Block persistingBlock) + internal static bool Check_SetRecord(DataCache snapshot, string name, RecordType type, string data, UInt160 signedBy, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); using var script = new ScriptBuilder(); @@ -195,7 +195,7 @@ internal static bool Check_SetRecord(StoreView snapshot, string name, RecordType return true; } - internal static BigInteger Check_Renew(StoreView snapshot, string name, UInt160 signedBy, Block persistingBlock) + internal static BigInteger Check_Renew(DataCache snapshot, string name, UInt160 signedBy, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); using var script = new ScriptBuilder(); @@ -215,7 +215,7 @@ internal static BigInteger Check_Renew(StoreView snapshot, string name, UInt160 return result.GetInteger(); } - internal static bool Check_SetAdmin(StoreView snapshot, string name, UInt160 admin, UInt160 signedBy, Block persistingBlock) + internal static bool Check_SetAdmin(DataCache snapshot, string name, UInt160 admin, UInt160 signedBy, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(admin, signedBy), snapshot, persistingBlock); using var script = new ScriptBuilder(); @@ -233,7 +233,7 @@ internal static bool Check_SetAdmin(StoreView snapshot, string name, UInt160 adm return true; } - internal static bool Check_Register(StoreView snapshot, string name, UInt160 owner, Block persistingBlock) + internal static bool Check_Register(DataCache snapshot, string name, UInt160 owner, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock); using var script = new ScriptBuilder(); @@ -254,7 +254,7 @@ internal static bool Check_Register(StoreView snapshot, string name, UInt160 own return result.GetBoolean(); } - internal static bool Check_SetPrice(StoreView snapshot, UInt160 signedBy, long price, Block persistingBlock) + internal static bool Check_SetPrice(DataCache snapshot, UInt160 signedBy, long price, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); using var script = new ScriptBuilder(); @@ -269,7 +269,7 @@ internal static bool Check_SetPrice(StoreView snapshot, UInt160 signedBy, long p return true; } - internal static bool Check_AddRoot(StoreView snapshot, UInt160 signedBy, string root, Block persistingBlock) + internal static bool Check_AddRoot(DataCache snapshot, UInt160 signedBy, string root, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); using var script = new ScriptBuilder(); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index ccb340864f..dcd67d350b 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -3,7 +3,6 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Caching; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -22,7 +21,7 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_NeoToken { - private StoreView _snapshot; + private DataCache _snapshot; private Block _persistingBlock; [TestInitialize] @@ -45,10 +44,10 @@ public void TestSetup() [TestMethod] public void Check_Vote() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000 }; - byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); // No signature @@ -80,7 +79,7 @@ public void Check_Vote() // no registered - var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, from)).GetInteroperable(); + var accountState = snapshot.TryGet(CreateStorageKey(20, from)).GetInteroperable(); accountState.VoteTo = null; ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeFalse(); @@ -89,7 +88,7 @@ public void Check_Vote() // normal case - snapshot.Storages.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); @@ -99,24 +98,24 @@ public void Check_Vote() [TestMethod] public void Check_Vote_Sameaccounts() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000 }; - byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); - var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, from)).GetInteroperable(); + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + var accountState = snapshot.TryGet(CreateStorageKey(20, from)).GetInteroperable(); accountState.Balance = 100; - snapshot.Storages.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); var ret = Check_Vote(snapshot, from, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); //two account vote for the same account - var stateValidator = snapshot.Storages.GetAndChange(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray())).GetInteroperable(); + var stateValidator = snapshot.GetAndChange(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray())).GetInteroperable(); stateValidator.Votes.Should().Be(100); var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); - snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); - var secondAccount = snapshot.Storages.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); + snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); + var secondAccount = snapshot.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); ret = Check_Vote(snapshot, G_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); @@ -126,57 +125,57 @@ public void Check_Vote_Sameaccounts() [TestMethod] public void Check_Vote_ChangeVote() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000 }; //from vote to G byte[] from = Blockchain.StandbyValidators[0].ToArray(); var from_Account = Contract.CreateSignatureContract(Blockchain.StandbyValidators[0]).ScriptHash.ToArray(); - snapshot.Storages.Add(CreateStorageKey(20, from_Account), new StorageItem(new NeoAccountState())); - var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); + snapshot.Add(CreateStorageKey(20, from_Account), new StorageItem(new NeoAccountState())); + var accountState = snapshot.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.Balance = 100; - snapshot.Storages.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); var ret = Check_Vote(snapshot, from_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); //from change vote to itself - var G_stateValidator = snapshot.Storages.GetAndChange(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray())).GetInteroperable(); + var G_stateValidator = snapshot.GetAndChange(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray())).GetInteroperable(); G_stateValidator.Votes.Should().Be(100); var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); - snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); - snapshot.Storages.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); + snapshot.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState())); ret = Check_Vote(snapshot, from_Account, from, true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); G_stateValidator.Votes.Should().Be(0); - var from_stateValidator = snapshot.Storages.GetAndChange(CreateStorageKey(33, from)).GetInteroperable(); + var from_stateValidator = snapshot.GetAndChange(CreateStorageKey(33, from)).GetInteroperable(); from_stateValidator.Votes.Should().Be(100); } [TestMethod] public void Check_Vote_VoteToNull() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000 }; byte[] from = Blockchain.StandbyValidators[0].ToArray(); var from_Account = Contract.CreateSignatureContract(Blockchain.StandbyValidators[0]).ScriptHash.ToArray(); - snapshot.Storages.Add(CreateStorageKey(20, from_Account), new StorageItem(new NeoAccountState())); - var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); + snapshot.Add(CreateStorageKey(20, from_Account), new StorageItem(new NeoAccountState())); + var accountState = snapshot.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.Balance = 100; - snapshot.Storages.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); var ret = Check_Vote(snapshot, from_Account, ECCurve.Secp256r1.G.ToArray(), true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); accountState.VoteTo.Should().Be(ECCurve.Secp256r1.G); //from vote to null account G votes becomes 0 - var G_stateValidator = snapshot.Storages.GetAndChange(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray())).GetInteroperable(); + var G_stateValidator = snapshot.GetAndChange(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray())).GetInteroperable(); G_stateValidator.Votes.Should().Be(100); var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); - snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); - snapshot.Storages.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState())); + snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState { Balance = 200 })); + snapshot.Add(CreateStorageKey(33, from), new StorageItem(new CandidateState())); ret = Check_Vote(snapshot, from_Account, null, true, persistingBlock); ret.Result.Should().BeTrue(); ret.State.Should().BeTrue(); @@ -187,10 +186,10 @@ public void Check_Vote_VoteToNull() [TestMethod] public void Check_UnclaimedGas() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000 }; - byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); var unclaim = Check_UnclaimedGas(snapshot, from, persistingBlock); unclaim.Value.Should().Be(new BigInteger(0.5 * 1000 * 100000000L)); @@ -204,16 +203,16 @@ public void Check_UnclaimedGas() [TestMethod] public void Check_RegisterValidator() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); - var keyCount = snapshot.Storages.GetChangeSet().Count(); + var keyCount = snapshot.GetChangeSet().Count(); var point = Blockchain.StandbyValidators[0].EncodePoint(true).Clone() as byte[]; var ret = Check_RegisterValidator(snapshot, point, _persistingBlock); // Exists ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); - snapshot.Storages.GetChangeSet().Count().Should().Be(++keyCount); // No changes + snapshot.GetChangeSet().Count().Should().Be(++keyCount); // No changes point[20]++; // fake point ret = Check_RegisterValidator(snapshot, point, _persistingBlock); // New @@ -221,7 +220,7 @@ public void Check_RegisterValidator() ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 1); // New validator + snapshot.GetChangeSet().Count().Should().Be(keyCount + 1); // New validator // Check GetRegisteredValidators @@ -232,50 +231,50 @@ public void Check_RegisterValidator() [TestMethod] public void Check_UnregisterCandidate() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); - var keyCount = snapshot.Storages.GetChangeSet().Count(); + var keyCount = snapshot.GetChangeSet().Count(); var point = Blockchain.StandbyValidators[0].EncodePoint(true); //without register var ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount); + snapshot.GetChangeSet().Count().Should().Be(keyCount); //register and then unregister ret = Check_RegisterValidator(snapshot, point, _persistingBlock); - StorageItem item = snapshot.Storages.GetAndChange(CreateStorageKey(33, point)); + StorageItem item = snapshot.GetAndChange(CreateStorageKey(33, point)); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); var members = NativeContract.NEO.GetCandidates(snapshot); Assert.AreEqual(1, members.Length); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 1); + snapshot.GetChangeSet().Count().Should().Be(keyCount + 1); StorageKey key = CreateStorageKey(33, point); - snapshot.Storages.TryGet(key).Should().NotBeNull(); + snapshot.TryGet(key).Should().NotBeNull(); ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount); + snapshot.GetChangeSet().Count().Should().Be(keyCount); members = NativeContract.NEO.GetCandidates(snapshot); Assert.AreEqual(0, members.Length); - snapshot.Storages.TryGet(key).Should().BeNull(); + snapshot.TryGet(key).Should().BeNull(); //register with votes, then unregister ret = Check_RegisterValidator(snapshot, point, _persistingBlock); var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); - snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState())); - var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); + snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState())); + var accountState = snapshot.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); accountState.Balance = 100; Check_Vote(snapshot, G_Account, Blockchain.StandbyValidators[0].ToArray(), true, _persistingBlock); ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); - snapshot.Storages.TryGet(key).Should().NotBeNull(); - StorageItem pointItem = snapshot.Storages.TryGet(key); + snapshot.TryGet(key).Should().NotBeNull(); + StorageItem pointItem = snapshot.TryGet(key); CandidateState pointState = pointItem.GetInteroperable(); pointState.Registered.Should().BeFalse(); pointState.Votes.Should().Be(100); @@ -290,15 +289,15 @@ public void Check_UnregisterCandidate() [TestMethod] public void Check_GetCommittee() { - var snapshot = _snapshot.Clone(); - var keyCount = snapshot.Storages.GetChangeSet().Count(); + var snapshot = _snapshot.CreateSnapshot(); + var keyCount = snapshot.GetChangeSet().Count(); var point = Blockchain.StandbyValidators[0].EncodePoint(true); var persistingBlock = _persistingBlock; //register with votes with 20000000 var G_Account = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash.ToArray(); - snapshot.Storages.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState())); - var accountState = snapshot.Storages.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); + snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState())); + var accountState = snapshot.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); accountState.Balance = 20000000; var ret = Check_RegisterValidator(snapshot, ECCurve.Secp256r1.G.ToArray(), persistingBlock); ret.State.Should().BeTrue(); @@ -320,14 +319,20 @@ public void Check_GetCommittee() { Index = (uint)ProtocolSettings.Default.CommitteeMembersCount, Transactions = Array.Empty(), - ConsensusData = new ConsensusData() + ConsensusData = new ConsensusData(), + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero, + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } }; for (int i = 0; i < ProtocolSettings.Default.CommitteeMembersCount - 1; i++) { - Check_RegisterValidator(snapshot, Blockchain.StandbyCommittee[i].ToArray(), persistingBlock); + ret = Check_RegisterValidator(snapshot, Blockchain.StandbyCommittee[i].ToArray(), persistingBlock); + ret.State.Should().BeTrue(); + ret.Result.Should().BeTrue(); } - Check_OnPersist(snapshot, persistingBlock); + Check_OnPersist(snapshot, persistingBlock).Should().BeTrue(); committeemembers = NativeContract.NEO.GetCommittee(snapshot); committeemembers.Length.Should().Be(ProtocolSettings.Default.CommitteeMembersCount); @@ -342,13 +347,13 @@ public void Check_GetCommittee() [TestMethod] public void Check_Transfer() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block() { Index = 1000 }; - byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); byte[] to = new byte[20]; - var keyCount = snapshot.Storages.GetChangeSet().Count(); + var keyCount = snapshot.GetChangeSet().Count(); // Check unclaim @@ -369,15 +374,15 @@ public void Check_Transfer() unclaim.Value.Should().Be(new BigInteger(0)); unclaim.State.Should().BeTrue(); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 4); // Gas + new balance + snapshot.GetChangeSet().Count().Should().Be(keyCount + 4); // Gas + new balance // Return balance - keyCount = snapshot.Storages.GetChangeSet().Count(); + keyCount = snapshot.GetChangeSet().Count(); NativeContract.NEO.Transfer(snapshot, to, from, BigInteger.One, true, persistingBlock).Should().BeTrue(); NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(0); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount - 1); // Remove neo balance from address two + snapshot.GetChangeSet().Count().Should().Be(keyCount - 1); // Remove neo balance from address two // Bad inputs @@ -393,8 +398,8 @@ public void Check_Transfer() [TestMethod] public void Check_BalanceOf() { - var snapshot = _snapshot.Clone(); - byte[] account = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); + var snapshot = _snapshot.CreateSnapshot(); + byte[] account = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); NativeContract.NEO.BalanceOf(snapshot, account).Should().Be(100_000_000); @@ -406,10 +411,19 @@ public void Check_BalanceOf() [TestMethod] public void Check_CommitteeBonus() { - var snapshot = _snapshot.Clone(); - var persistingBlock = new Block { Index = 1, Transactions = Array.Empty() }; + var snapshot = _snapshot.CreateSnapshot(); + var persistingBlock = new Block + { + Index = 1, + Transactions = Array.Empty(), + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + ConsensusData = new ConsensusData(), + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }; - Check_PostPersist(snapshot, persistingBlock); + Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); var committee = Blockchain.StandbyCommittee; NativeContract.GAS.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[0]).ScriptHash.ToArray()).Should().Be(50000000); @@ -420,7 +434,7 @@ public void Check_CommitteeBonus() [TestMethod] public void Check_Initialize() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); // StandbyValidators @@ -442,70 +456,70 @@ public void Check_BadScript() [TestMethod] public void TestCalculateBonus() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Index = 0 }; StorageKey key = CreateStorageKey(20, UInt160.Zero.ToArray()); // Fault: balance < 0 - snapshot.Storages.Add(key, new StorageItem(new NeoAccountState + snapshot.Add(key, new StorageItem(new NeoAccountState { Balance = -100 })); Action action = () => NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); action.Should().Throw(); - snapshot.Storages.Delete(key); + snapshot.Delete(key); // Fault range: start >= end - snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoAccountState + snapshot.GetAndChange(key, () => new StorageItem(new NeoAccountState { Balance = 100, BalanceHeight = 100 })); action = () => NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); - snapshot.Storages.Delete(key); + snapshot.Delete(key); // Fault range: start >= end - snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoAccountState + snapshot.GetAndChange(key, () => new StorageItem(new NeoAccountState { Balance = 100, BalanceHeight = 100 })); action = () => NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); - snapshot.Storages.Delete(key); + snapshot.Delete(key); // Normal 1) votee is non exist - snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoAccountState + snapshot.GetAndChange(key, () => new StorageItem(new NeoAccountState { Balance = 100 })); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 100).Should().Be(new BigInteger(0.5 * 100 * 100)); - snapshot.Storages.Delete(key); + snapshot.Delete(key); // Normal 2) votee is not committee - snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoAccountState + snapshot.GetAndChange(key, () => new StorageItem(new NeoAccountState { Balance = 100, VoteTo = ECCurve.Secp256r1.G })); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 100).Should().Be(new BigInteger(0.5 * 100 * 100)); - snapshot.Storages.Delete(key); + snapshot.Delete(key); // Normal 3) votee is committee - snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoAccountState + snapshot.GetAndChange(key, () => new StorageItem(new NeoAccountState { Balance = 100, VoteTo = Blockchain.StandbyCommittee[0] })); - snapshot.Storages.Add(new KeyBuilder(-2, 23).Add(Blockchain.StandbyCommittee[0]).AddBigEndian(uint.MaxValue - 50), new StorageItem() { Value = new BigInteger(50 * 10000L).ToByteArray() }); + snapshot.Add(new KeyBuilder(NativeContract.NEO.Id, 23).Add(Blockchain.StandbyCommittee[0]).AddBigEndian(uint.MaxValue - 50), new StorageItem() { Value = new BigInteger(50 * 10000L).ToByteArray() }); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 100).Should().Be(new BigInteger(50 * 100)); - snapshot.Storages.Delete(key); + snapshot.Delete(key); } [TestMethod] @@ -529,7 +543,7 @@ public void TestGetNextBlockValidators1() [TestMethod] public void TestGetNextBlockValidators2() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var result = NativeContract.NEO.GetNextBlockValidators(snapshot); result.Length.Should().Be(7); result[0].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); @@ -552,54 +566,63 @@ public void TestGetCandidates1() [TestMethod] public void TestGetCandidates2() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var result = NativeContract.NEO.GetCandidates(snapshot); result.Length.Should().Be(0); StorageKey key = NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G); - snapshot.Storages.Add(key, new StorageItem(new CandidateState())); + snapshot.Add(key, new StorageItem(new CandidateState())); NativeContract.NEO.GetCandidates(snapshot).Length.Should().Be(1); } [TestMethod] public void TestCheckCandidate() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var committee = NativeContract.NEO.GetCommittee(snapshot); var point = committee[0].EncodePoint(true); // Prepare Prefix_VoterRewardPerCommittee - var storageKey = new KeyBuilder(-2, 23).Add(committee[0]).AddBigEndian(20); - snapshot.Storages.Add(storageKey, new StorageItem(new BigInteger(1000))); + var storageKey = new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[0]).AddBigEndian(20); + snapshot.Add(storageKey, new StorageItem(new BigInteger(1000))); // Prepare Candidate - storageKey = new KeyBuilder(-2, 33).Add(committee[0]); - snapshot.Storages.Add(storageKey, new StorageItem(new CandidateState { Registered = true, Votes = BigInteger.One })); + storageKey = new KeyBuilder(NativeContract.NEO.Id, 33).Add(committee[0]); + snapshot.Add(storageKey, new StorageItem(new CandidateState { Registered = true, Votes = BigInteger.One })); - storageKey = new KeyBuilder(-2, 23).Add(committee[0]); - snapshot.Storages.Find(storageKey.ToArray()).ToArray().Length.Should().Be(1); + storageKey = new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[0]); + snapshot.Find(storageKey.ToArray()).ToArray().Length.Should().Be(1); // Pre-persist - var persistingBlock = new Block { Index = 21, ConsensusData = new ConsensusData(), Transactions = Array.Empty() }; - Check_OnPersist(snapshot, persistingBlock); + var persistingBlock = new Block + { + Index = 21, + ConsensusData = new ConsensusData(), + Transactions = Array.Empty(), + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }; + Check_OnPersist(snapshot, persistingBlock).Should().BeTrue(); // Clear votes - storageKey = new KeyBuilder(-2, 33).Add(committee[0]); - snapshot.Storages.GetAndChange(storageKey).GetInteroperable().Votes = BigInteger.Zero; + storageKey = new KeyBuilder(NativeContract.NEO.Id, 33).Add(committee[0]); + snapshot.GetAndChange(storageKey).GetInteroperable().Votes = BigInteger.Zero; // Unregister candidate, remove var ret = Check_UnregisterCandidate(snapshot, point, persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); - storageKey = new KeyBuilder(-2, 23).Add(committee[0]); - snapshot.Storages.Find(storageKey.ToArray()).ToArray().Length.Should().Be(0); + storageKey = new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[0]); + snapshot.Find(storageKey.ToArray()).ToArray().Length.Should().Be(0); // Post-persist Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); - storageKey = new KeyBuilder(-2, 23).Add(committee[0]); - snapshot.Storages.Find(storageKey.ToArray()).ToArray().Length.Should().Be(1); + storageKey = new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[0]); + snapshot.Find(storageKey.ToArray()).ToArray().Length.Should().Be(1); } [TestMethod] @@ -637,7 +660,7 @@ public void TestGetCommittee() [TestMethod] public void TestGetValidators() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var result = NativeContract.NEO.ComputeNextBlockValidators(snapshot); result[0].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); result[1].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); @@ -667,14 +690,15 @@ public void TestOnBalanceChanging() [TestMethod] public void TestTotalSupply() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); NativeContract.NEO.TotalSupply(snapshot).Should().Be(new BigInteger(100000000)); } [TestMethod] public void TestEconomicParameter() { - var snapshot = _snapshot.Clone(); + const byte Prefix_CurrentBlock = 12; + var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Index = 0 }; (BigInteger, bool) result = Check_GetGasPerBlock(snapshot, persistingBlock); @@ -686,46 +710,56 @@ public void TestEconomicParameter() result1.Item2.Should().BeTrue(); result1.Item1.GetBoolean().Should().BeTrue(); - snapshot.BlockHashIndex.GetAndChange().Index = persistingBlock.Index + 1; + var height = snapshot[NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable(); + height.Index = persistingBlock.Index + 1; result = Check_GetGasPerBlock(snapshot, persistingBlock); result.Item2.Should().BeTrue(); result.Item1.Should().Be(10 * NativeContract.GAS.Factor); // Check calculate bonus - StorageItem storage = snapshot.Storages.GetOrAdd(CreateStorageKey(20, UInt160.Zero.ToArray()), () => new StorageItem(new NeoAccountState())); + StorageItem storage = snapshot.GetOrAdd(CreateStorageKey(20, UInt160.Zero.ToArray()), () => new StorageItem(new NeoAccountState())); NeoAccountState state = storage.GetInteroperable(); state.Balance = 1000; state.BalanceHeight = 0; - snapshot.BlockHashIndex.GetAndChange().Index = 0; // Fake Height=0 + height.Index = 0; // Fake Height=0 NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, persistingBlock.Index + 2).Should().Be(6500); } [TestMethod] public void TestClaimGas() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); // Initialize block - snapshot.Storages.Add(CreateStorageKey(1), new StorageItem(new BigInteger(30000000))); + snapshot.Add(CreateStorageKey(1), new StorageItem(new BigInteger(30000000))); ECPoint[] standbyCommittee = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray(); CachedCommittee cachedCommittee = new CachedCommittee(); for (var i = 0; i < ProtocolSettings.Default.CommitteeMembersCount; i++) { ECPoint member = standbyCommittee[i]; - snapshot.Storages.Add(new KeyBuilder(-2, 33).Add(member), new StorageItem(new CandidateState() + snapshot.Add(new KeyBuilder(NativeContract.NEO.Id, 33).Add(member), new StorageItem(new CandidateState() { Registered = true, Votes = 200 * 10000 })); cachedCommittee.Add((member, 200 * 10000)); } - snapshot.Storages[new KeyBuilder(-2, 14)].Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), 4096); + snapshot.GetOrAdd(new KeyBuilder(NativeContract.NEO.Id, 14), () => new StorageItem()).Value = BinarySerializer.Serialize(cachedCommittee.ToStackItem(null), 4096); - var item = snapshot.Storages.GetAndChange(new KeyBuilder(-2, 1), () => new StorageItem()); + var item = snapshot.GetAndChange(new KeyBuilder(NativeContract.NEO.Id, 1), () => new StorageItem()); item.Value = ((BigInteger)2100 * 10000L).ToByteArray(); - var persistingBlock = new Block { Index = 0, Transactions = Array.Empty() }; + var persistingBlock = new Block + { + Index = 0, + Transactions = Array.Empty(), + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + ConsensusData = new ConsensusData(), + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); var committee = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray(); @@ -733,36 +767,54 @@ public void TestClaimGas() var accountB = committee[ProtocolSettings.Default.CommitteeMembersCount - 1]; NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(accountA).ScriptHash).Should().Be(0); - StorageItem storageItem = snapshot.Storages.TryGet(new KeyBuilder(-2, 23).Add(accountA).AddBigEndian(1)); + StorageItem storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(accountA).AddBigEndian(1)); new BigInteger(storageItem.Value).Should().Be(30000000000); - snapshot.Storages.TryGet(new KeyBuilder(-2, 23).Add(accountB).AddBigEndian(uint.MaxValue - 1)).Should().BeNull(); + snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(accountB).AddBigEndian(uint.MaxValue - 1)).Should().BeNull(); // Next block - persistingBlock = new Block { Index = 1, Transactions = Array.Empty() }; + persistingBlock = new Block + { + Index = 1, + Transactions = Array.Empty(), + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + ConsensusData = new ConsensusData(), + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[1]).ScriptHash).Should().Be(0); - storageItem = snapshot.Storages.TryGet(new KeyBuilder(-2, 23).Add(committee[1]).AddBigEndian(1)); + storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[1]).AddBigEndian(1)); new BigInteger(storageItem.Value).Should().Be(30000000000); // Next block - persistingBlock = new Block { Index = 21, Transactions = Array.Empty() }; + persistingBlock = new Block + { + Index = 21, + Transactions = Array.Empty(), + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + ConsensusData = new ConsensusData(), + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); accountA = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray()[2]; NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[2]).ScriptHash).Should().Be(0); - storageItem = snapshot.Storages.TryGet(new KeyBuilder(-2, 23).Add(committee[2]).AddBigEndian(22)); + storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[2]).AddBigEndian(22)); new BigInteger(storageItem.Value).Should().Be(30000000000 * 2); // Claim GAS var account = Contract.CreateSignatureContract(committee[2]).ScriptHash; - snapshot.Storages.Add(new KeyBuilder(-2, 20).Add(account), new StorageItem(new NeoAccountState + snapshot.Add(new KeyBuilder(NativeContract.NEO.Id, 20).Add(account), new StorageItem(new NeoAccountState { BalanceHeight = 3, Balance = 200 * 10000 - 2 * 100, @@ -776,16 +828,16 @@ public void TestClaimGas() [TestMethod] public void TestUnclaimedGas() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); - snapshot.Storages.Add(CreateStorageKey(20, UInt160.Zero.ToArray()), new StorageItem(new NeoAccountState())); + snapshot.Add(CreateStorageKey(20, UInt160.Zero.ToArray()), new StorageItem(new NeoAccountState())); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); } [TestMethod] public void TestVote() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); UInt160 account = UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4"); StorageKey keyAccount = CreateStorageKey(20, account.ToArray()); StorageKey keyValidator = CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()); @@ -797,17 +849,17 @@ public void TestVote() ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); - snapshot.Storages.Add(keyAccount, new StorageItem(new NeoAccountState())); + snapshot.Add(keyAccount, new StorageItem(new NeoAccountState())); ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); - snapshot.Storages.Delete(keyAccount); - snapshot.Storages.GetAndChange(keyAccount, () => new StorageItem(new NeoAccountState + snapshot.Delete(keyAccount); + snapshot.GetAndChange(keyAccount, () => new StorageItem(new NeoAccountState { VoteTo = ECCurve.Secp256r1.G })); - snapshot.Storages.Add(keyValidator, new StorageItem(new CandidateState())); + snapshot.Add(keyValidator, new StorageItem(new CandidateState())); ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); @@ -815,23 +867,23 @@ public void TestVote() internal (bool State, bool Result) Transfer4TesingOnBalanceChanging(BigInteger amount, bool addVotes) { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var engine = ApplicationEngine.Create(TriggerType.Application, Blockchain.GenesisBlock, snapshot, Blockchain.GenesisBlock); ScriptBuilder sb = new ScriptBuilder(); var tmp = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); UInt160 from = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot)[0]; if (addVotes) { - snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new NeoAccountState + snapshot.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new NeoAccountState { VoteTo = ECCurve.Secp256r1.G, Balance = new BigInteger(1000) })); - snapshot.Storages.Add(NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G), new StorageItem(new CandidateState())); + snapshot.Add(NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G), new StorageItem(new CandidateState())); } else { - snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new NeoAccountState + snapshot.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new NeoAccountState { Balance = new BigInteger(1000) })); @@ -845,7 +897,7 @@ public void TestVote() return (true, result.GetBoolean()); } - internal static bool Check_OnPersist(StoreView snapshot, Block persistingBlock) + internal static bool Check_OnPersist(DataCache snapshot, Block persistingBlock) { var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_NativeOnPersist); @@ -855,7 +907,7 @@ internal static bool Check_OnPersist(StoreView snapshot, Block persistingBlock) return engine.Execute() == VMState.HALT; } - internal static bool Check_PostPersist(StoreView snapshot, Block persistingBlock) + internal static bool Check_PostPersist(DataCache snapshot, Block persistingBlock) { using var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_NativePostPersist); @@ -865,7 +917,7 @@ internal static bool Check_PostPersist(StoreView snapshot, Block persistingBlock return engine.Execute() == VMState.HALT; } - internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView snapshot, Block persistingBlock) + internal static (BigInteger Value, bool State) Check_GetGasPerBlock(DataCache snapshot, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); @@ -885,7 +937,7 @@ internal static (BigInteger Value, bool State) Check_GetGasPerBlock(StoreView sn return (((VM.Types.Integer)result).GetInteger(), true); } - internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreView snapshot, BigInteger gasPerBlock, Block persistingBlock) + internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(DataCache snapshot, BigInteger gasPerBlock, Block persistingBlock) { UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot, persistingBlock); @@ -908,7 +960,7 @@ internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(StoreV return (((VM.Types.Boolean)result).GetBoolean(), true); } - internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[] pubkey, bool signAccount, Block persistingBlock) + internal static (bool State, bool Result) Check_Vote(DataCache snapshot, byte[] account, byte[] pubkey, bool signAccount, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, persistingBlock); @@ -936,7 +988,7 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] return (true, result.GetBoolean()); } - internal static (bool State, bool Result) Check_RegisterValidator(StoreView snapshot, byte[] pubkey, Block persistingBlock) + internal static (bool State, bool Result) Check_RegisterValidator(DataCache snapshot, byte[] pubkey, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock, 1100_00000000); @@ -959,7 +1011,7 @@ internal static (bool State, bool Result) Check_RegisterValidator(StoreView snap return (true, result.GetBoolean()); } - internal static ECPoint[] Check_GetCommittee(StoreView snapshot, Block persistingBlock) + internal static ECPoint[] Check_GetCommittee(DataCache snapshot, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); @@ -977,7 +1029,7 @@ internal static ECPoint[] Check_GetCommittee(StoreView snapshot, Block persistin return (result as VM.Types.Array).Select(u => u.GetSpan().AsSerializable()).ToArray(); } - internal static (BigInteger Value, bool State) Check_UnclaimedGas(StoreView snapshot, byte[] address, Block persistingBlock) + internal static (BigInteger Value, bool State) Check_UnclaimedGas(DataCache snapshot, byte[] address, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); @@ -1000,7 +1052,7 @@ internal static (BigInteger Value, bool State) Check_UnclaimedGas(StoreView snap return (result.GetInteger(), true); } - internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable) + internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable) { var st = new BigInteger(trackable.Item.Value); st.Should().Be(0); @@ -1009,7 +1061,7 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint voteTo) + internal static void CheckBalance(byte[] account, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint voteTo) { var st = (VM.Types.Struct)BinarySerializer.Deserialize(trackable.Item.Value, 16, 32); @@ -1036,7 +1088,7 @@ internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) return storageKey; } - internal static (bool State, bool Result) Check_UnregisterCandidate(StoreView snapshot, byte[] pubkey, Block persistingBlock) + internal static (bool State, bool Result) Check_UnregisterCandidate(DataCache snapshot, byte[] pubkey, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index d0dee61fc8..c84807442b 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -7,6 +7,7 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; +using Neo.UnitTests.IO.Caching; using System; using System.Linq; @@ -15,7 +16,7 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_PolicyContract { - private StoreView _snapshot; + private DataCache _snapshot; [TestInitialize] public void TestSetup() @@ -30,7 +31,7 @@ public void TestSetup() [TestMethod] public void Check_Default() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); var ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); @@ -52,13 +53,11 @@ public void Check_Default() [TestMethod] public void Check_SetMaxBlockSize() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); // Without signature @@ -99,12 +98,11 @@ public void Check_SetMaxBlockSize() [TestMethod] public void Check_SetMaxBlockSystemFee() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); @@ -146,12 +144,11 @@ public void Check_SetMaxBlockSystemFee() [TestMethod] public void Check_SetMaxTransactionsPerBlock() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new TrimmedBlock() { NextConsensus = UInt160.Zero }); // Without signature @@ -179,12 +176,11 @@ public void Check_SetMaxTransactionsPerBlock() [TestMethod] public void Check_SetFeePerByte() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new TrimmedBlock() { NextConsensus = UInt160.Zero }); // Without signature @@ -217,8 +213,6 @@ public void Check_SetBaseExecFee() // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - NativeContract.Policy.Initialize(ApplicationEngine.Create(TriggerType.Application, null, snapshot, block, 0)); // Without signature @@ -263,8 +257,6 @@ public void Check_SetStoragePrice() // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - NativeContract.Policy.Initialize(ApplicationEngine.Create(TriggerType.Application, null, snapshot, block, 0)); // Without signature @@ -304,12 +296,11 @@ public void Check_SetStoragePrice() [TestMethod] public void Check_BlockAccount() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new TrimmedBlock() { NextConsensus = UInt160.Zero }); // Without signature @@ -353,13 +344,11 @@ public void Check_BlockAccount() [TestMethod] public void Check_Block_UnblockAccount() { - var snapshot = _snapshot.Clone(); + var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new TrimmedBlock() { NextConsensus = UInt160.Zero }); - UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); // Block without signature diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs index 24db8906f5..d4088258f8 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs @@ -16,7 +16,7 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_RoleManagement { - private StoreView _snapshot; + private DataCache _snapshot; [TestInitialize] public void TestSetup() @@ -28,7 +28,7 @@ public void TestSetup() [TestMethod] public void TestSetAndGet() { - var snapshot1 = _snapshot.Clone(); + var snapshot1 = _snapshot.CreateSnapshot(); UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot1); ECPoint[] validators = NativeContract.NEO.ComputeNextBlockValidators(snapshot1); var ret = NativeContract.RoleManagement.Call( @@ -40,7 +40,7 @@ public void TestSetAndGet() new ContractParameter(ContractParameterType.Array) { Value = validators.Select(p => new ContractParameter(ContractParameterType.ByteArray) { Value = p.ToArray() }).ToList() } ); snapshot1.Commit(); - var snapshot2 = _snapshot.Clone(); + var snapshot2 = _snapshot.CreateSnapshot(); ret = NativeContract.RoleManagement.Call( snapshot2, "getDesignatedByRole", diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs index ad8e37b107..ce0cbf9677 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs @@ -63,7 +63,7 @@ public void TestCanResetAppEngineProviderTwice() class TestProvider : IApplicationEngineProvider { - public ApplicationEngine Create(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock, long gas) + public ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, long gas) { return new TestEngine(trigger, container, snapshot, persistingBlock, gas); } @@ -71,7 +71,7 @@ public ApplicationEngine Create(TriggerType trigger, IVerifiable container, Stor class TestEngine : ApplicationEngine { - public TestEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, Block persistingBlock, long gas) + public TestEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, long gas) : base(trigger, container, snapshot, persistingBlock, gas) { } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index 53b8f559af..24c9907029 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -61,7 +61,7 @@ public void ApplicationEngineRegularPut() StorageItem sItem = TestUtils.GetStorageItem(new byte[0] { }); var snapshot = Blockchain.Singleton.GetSnapshot(); - snapshot.Storages.Add(skey, sItem); + snapshot.Add(skey, sItem); snapshot.AddContract(script.ToScriptHash(), contractState); using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshot)) @@ -94,7 +94,7 @@ public void ApplicationEngineReusedStorage_FullReuse() StorageItem sItem = TestUtils.GetStorageItem(value); var snapshot = Blockchain.Singleton.GetSnapshot(); - snapshot.Storages.Add(skey, sItem); + snapshot.Add(skey, sItem); snapshot.AddContract(script.ToScriptHash(), contractState); using (ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.Application, null, snapshot)) @@ -129,7 +129,7 @@ public void ApplicationEngineReusedStorage_PartialReuse() StorageItem sItem = TestUtils.GetStorageItem(oldValue); var snapshot = Blockchain.Singleton.GetSnapshot(); - snapshot.Storages.Add(skey, sItem); + snapshot.Add(skey, sItem); snapshot.AddContract(script.ToScriptHash(), contractState); using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshot)) @@ -165,7 +165,7 @@ public void ApplicationEngineReusedStorage_PartialReuseTwice() StorageItem sItem = TestUtils.GetStorageItem(oldValue); var snapshot = Blockchain.Singleton.GetSnapshot(); - snapshot.Storages.Add(skey, sItem); + snapshot.Add(skey, sItem); snapshot.AddContract(script.ToScriptHash(), contractState); using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, snapshot)) diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index d268840d64..d721c3936a 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -114,7 +114,7 @@ public void TestAccount_IsStandard() 0x01, 0x01, 0x01, 0x01, 0x01 }; engine.IsStandardContract(new UInt160(hash)).Should().BeFalse(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); var state = TestUtils.GetContract(); snapshot.AddContract(state.Hash, state); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); @@ -130,7 +130,7 @@ public void TestAccount_IsStandard() [TestMethod] public void TestContract_Create() { - var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); var nef = new NefFile() { Script = new byte[byte.MaxValue], @@ -173,7 +173,7 @@ public void TestContract_Create() [TestMethod] public void TestContract_Update() { - var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); var nef = new NefFile() { Script = new byte[] { 0x01 }, @@ -211,11 +211,11 @@ public void TestContract_Update() Key = new byte[] { 0x01 } }; snapshot.AddContract(state.Hash, state); - snapshot.Storages.Add(storageKey, storageItem); + snapshot.Add(storageKey, storageItem); state.UpdateCounter.Should().Be(0); snapshot.UpdateContract(state.Hash, nef.ToArray(), manifest.ToJson().ToByteArray(false)); var ret = NativeContract.ContractManagement.GetContract(snapshot, state.Hash); - snapshot.Storages.Find(BitConverter.GetBytes(state.Id)).ToList().Count().Should().Be(1); + snapshot.Find(BitConverter.GetBytes(state.Id)).ToList().Count().Should().Be(1); ret.UpdateCounter.Should().Be(1); ret.Id.Should().Be(state.Id); ret.Manifest.ToJson().ToString().Should().Be(manifest.ToJson().ToString()); @@ -233,7 +233,7 @@ public void TestContract_Update_Invalid() }; nefFile.CheckSum = NefFile.ComputeChecksum(nefFile); - var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); Assert.ThrowsException(() => snapshot.UpdateContract(null, null, new byte[] { 0x01 })); Assert.ThrowsException(() => snapshot.UpdateContract(null, nefFile.ToArray(), null)); @@ -254,7 +254,7 @@ public void TestContract_Update_Invalid() [TestMethod] public void TestStorage_Find() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); var state = TestUtils.GetContract(); var storageItem = new StorageItem @@ -268,7 +268,7 @@ public void TestStorage_Find() Key = new byte[] { 0x01 } }; snapshot.AddContract(state.Hash, state); - snapshot.Storages.Add(storageKey, storageItem); + snapshot.Add(storageKey, storageItem); var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index d5e70f1a23..2ef9ac2de8 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -8,6 +8,7 @@ using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; using Neo.VM; using Neo.VM.Types; @@ -183,7 +184,7 @@ public void Runtime_GetNotifications_Test() snapshot.DeleteContract(scriptHash2); } - private void AssertNotification(StackItem stackItem, UInt160 scriptHash, string notification) + private static void AssertNotification(StackItem stackItem, UInt160 scriptHash, string notification) { Assert.IsInstanceOfType(stackItem, typeof(VM.Types.Array)); @@ -259,10 +260,10 @@ public void TestRuntime_CheckWitness() engine.CheckWitness(pubkey.EncodePoint(true)).Should().BeTrue(); engine.CheckWitness(((Transaction)engine.ScriptContainer).Sender.ToArray()).Should().BeTrue(); - ((Transaction)engine.ScriptContainer).Signers = new Signer[0]; + ((Transaction)engine.ScriptContainer).Signers = System.Array.Empty(); engine.CheckWitness(pubkey.EncodePoint(true)).Should().BeFalse(); - Action action = () => engine.CheckWitness(new byte[0]); + Action action = () => engine.CheckWitness(System.Array.Empty()); action.Should().Throw(); } @@ -336,7 +337,8 @@ public void TestCrypto_Verify() [TestMethod] public void TestBlockchain_GetHeight() { - GetEngine(true, true).GetBlockchainHeight().Should().Be(0); + var engine = GetEngine(true, true); + NativeContract.Ledger.CurrentIndex(engine.Snapshot).Should().Be(0); } [TestMethod] @@ -344,16 +346,16 @@ public void TestBlockchain_GetBlock() { var engine = GetEngine(true, true); - engine.GetBlock(new byte[] { 0x01 }).Should().BeNull(); + NativeContract.Ledger.GetBlock(engine.Snapshot, UInt256.Zero).Should().BeNull(); byte[] data1 = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; - engine.GetBlock(data1).Should().BeNull(); + NativeContract.Ledger.GetBlock(engine.Snapshot, new UInt256(data1)).Should().BeNull(); byte[] data2 = new byte[] { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; - Assert.ThrowsException(() => engine.GetBlock(data2)); + Assert.ThrowsException(() => NativeContract.Ledger.GetBlock(engine.Snapshot, new UInt256(data2))); } [TestMethod] @@ -364,18 +366,31 @@ public void TestBlockchain_GetTransaction() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; - engine.GetTransaction(new UInt256(data1)).Should().BeNull(); + NativeContract.Ledger.GetTransaction(engine.Snapshot, new UInt256(data1)).Should().BeNull(); } [TestMethod] public void TestBlockchain_GetTransactionHeight() { - var engine = GetEngine(true, true); - byte[] data1 = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; - engine.GetTransactionHeight(new UInt256(data1)).Should().Be(-1); + var engine = GetEngine(hasSnapshot: true, addScript: false); + var state = new TransactionState() + { + BlockIndex = 0, + Transaction = TestUtils.CreateRandomHashTransaction() + }; + UT_SmartContractHelper.TransactionAdd(engine.Snapshot, state); + engine.LoadScript(NativeContract.Ledger.Script, configureState: p => p.ScriptHash = NativeContract.Ledger.Hash); + + var script = new ScriptBuilder(); + script.EmitPush(state.Transaction.Hash.ToArray()); + script.EmitPush("getTransactionHeight"); + engine.LoadScript(script.ToArray()); + engine.Execute(); + Assert.AreEqual(engine.State, VMState.HALT); + + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Integer)); + result.GetInteger().Should().Be(0); } [TestMethod] @@ -386,14 +401,14 @@ public void TestBlockchain_GetContract() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; - Neo.SmartContract.Native.NativeContract.ContractManagement.GetContract(engine.Snapshot, new UInt160(data1)).Should().BeNull(); + NativeContract.ContractManagement.GetContract(engine.Snapshot, new UInt160(data1)).Should().BeNull(); var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); snapshot.AddContract(state.Hash, state); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); - Neo.SmartContract.Native.NativeContract.ContractManagement.GetContract(engine.Snapshot, state.Hash).Should().BeSameAs(state); + NativeContract.ContractManagement.GetContract(engine.Snapshot, state.Hash).Should().BeSameAs(state); } [TestMethod] @@ -434,7 +449,7 @@ public void TestStorage_Get() IsConstant = true }; snapshot.AddContract(state.Hash, state); - snapshot.Storages.Add(storageKey, storageItem); + snapshot.Add(storageKey, storageItem); var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); @@ -491,7 +506,7 @@ public void TestStorage_Put() IsConstant = true }; snapshot.AddContract(state.Hash, state); - snapshot.Storages.Add(storageKey, storageItem); + snapshot.Add(storageKey, storageItem); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); key = new byte[] { 0x01 }; @@ -505,7 +520,7 @@ public void TestStorage_Put() //value length == 0 key = new byte[] { 0x01 }; - value = new byte[0]; + value = System.Array.Empty(); engine.Put(storageContext, key, value); } @@ -525,7 +540,7 @@ public void TestStorage_PutEx() IsConstant = false }; snapshot.AddContract(state.Hash, state); - snapshot.Storages.Add(storageKey, storageItem); + snapshot.Add(storageKey, storageItem); var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); var key = new byte[] { 0x01 }; @@ -555,7 +570,7 @@ public void TestStorage_Delete() IsConstant = false }; snapshot.AddContract(state.Hash, state); - snapshot.Storages.Add(storageKey, storageItem); + snapshot.Add(storageKey, storageItem); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); var key = new byte[] { 0x01 }; @@ -612,7 +627,7 @@ public void TestContract_Call() [TestMethod] public void TestContract_Destroy() { - var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); var scriptHash = UInt160.Parse("0xcb9f3b7c6fb1cf2c13a40637c189bdd066a272b4"); var storageItem = new StorageItem @@ -627,15 +642,15 @@ public void TestContract_Destroy() Key = new byte[] { 0x01 } }; snapshot.AddContract(scriptHash, state); - snapshot.Storages.Add(storageKey, storageItem); + snapshot.Add(storageKey, storageItem); snapshot.DestroyContract(scriptHash); - snapshot.Storages.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); + snapshot.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); //storages are removed state = TestUtils.GetContract(); snapshot.AddContract(scriptHash, state); snapshot.DestroyContract(scriptHash); - snapshot.Storages.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); + snapshot.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); } [TestMethod] @@ -655,7 +670,7 @@ public static void LogEvent(object sender, LogEventArgs args) private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSnapshot = false, bool hasBlock = false, bool addScript = true, long gas = 20_00000000) { var tx = hasContainer ? TestUtils.GetTransaction(UInt160.Zero) : null; - var snapshot = hasSnapshot ? Blockchain.Singleton.GetSnapshot().Clone() : null; + var snapshot = hasSnapshot ? Blockchain.Singleton.GetSnapshot() : null; var block = hasBlock ? new Block() : null; ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, block, gas); if (addScript) engine.LoadScript(new byte[] { 0x01 }); diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index a23513f2a2..970f29720a 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -1,7 +1,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; +using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; using Neo.Wallets; using System; @@ -14,6 +17,10 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_SmartContractHelper { + const byte Prefix_Block = 5; + const byte Prefix_BlockHash = 9; + const byte Prefix_Transaction = 11; + [TestInitialize] public void TestSetup() { @@ -116,17 +123,34 @@ public void TestIsStandardContract() [TestMethod] public void TestVerifyWitnesses() { - var snapshot1 = Blockchain.Singleton.GetSnapshot(); + var snapshot1 = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); UInt256 index1 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); - snapshot1.Blocks.Add(index1, new TrimmedBlock()); - snapshot1.Blocks.Delete(index1); + BlocksAdd(snapshot1, index1, new TrimmedBlock() + { + Timestamp = 1, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + ConsensusData = new ConsensusData(), + Hashes = new UInt256[1] { UInt256.Zero }, + NextConsensus = UInt160.Zero, + Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } + }); + BlocksDelete(snapshot1, index1); Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, snapshot1, 100)); var snapshot2 = Blockchain.Singleton.GetSnapshot(); UInt256 index2 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); - TrimmedBlock block2 = new TrimmedBlock(); - block2.NextConsensus = UInt160.Zero; - snapshot2.Blocks.Add(index2, block2); + TrimmedBlock block2 = new TrimmedBlock() + { + Timestamp = 2, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + ConsensusData = new ConsensusData(), + Hashes = new UInt256[1] { UInt256.Zero }, + NextConsensus = UInt160.Zero, + Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } + }; + BlocksAdd(snapshot2, index2, block2); Header header2 = new Header() { PrevHash = index2, Witness = new Witness { VerificationScript = new byte[0] } }; snapshot2.AddContract(UInt160.Zero, new ContractState()); @@ -135,9 +159,17 @@ public void TestVerifyWitnesses() var snapshot3 = Blockchain.Singleton.GetSnapshot(); UInt256 index3 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); - TrimmedBlock block3 = new TrimmedBlock(); - block3.NextConsensus = UInt160.Zero; - snapshot3.Blocks.Add(index3, block3); + TrimmedBlock block3 = new TrimmedBlock() + { + Timestamp = 3, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + ConsensusData = new ConsensusData(), + Hashes = new UInt256[1] { UInt256.Zero }, + NextConsensus = UInt160.Zero, + Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } + }; + BlocksAdd(snapshot3, index3, block3); Header header3 = new Header() { PrevHash = index3, @@ -164,12 +196,32 @@ public void TestVerifyWitnesses() Manifest = TestUtils.CreateManifest("verify", ContractParameterType.Boolean, ContractParameterType.Signature), // Offset = 0 }; snapshot3.AddContract(contract.Hash, contract); - var tx = new Extensions.Nep17NativeContractExtensions.ManualWitness(contract.Hash) + var tx = new Nep17NativeContractExtensions.ManualWitness(contract.Hash) { - Witnesses = new Witness[] { new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } } + Witnesses = new Witness[] { new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } } }; Assert.AreEqual(true, Neo.SmartContract.Helper.VerifyWitnesses(tx, snapshot3, 1000)); } + + private void BlocksDelete(DataCache snapshot, UInt256 hash) + { + snapshot.Delete(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, hash)); + snapshot.Delete(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash)); + } + + public static void TransactionAdd(DataCache snapshot, params TransactionState[] txs) + { + foreach (TransactionState tx in txs) + { + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Transaction, tx.Transaction.Hash), new StorageItem(tx, true)); + } + } + + public static void BlocksAdd(DataCache snapshot, UInt256 hash, TrimmedBlock block) + { + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, block.Index), new StorageItem(hash.ToArray(), true)); + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash), new StorageItem(block.ToArray(), true)); + } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index ed7abb463e..7ce2aed12c 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -3,6 +3,7 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; +using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; using Neo.VM; using Neo.VM.Types; @@ -36,7 +37,7 @@ public void System_Blockchain_GetBlock() Witnesses = new Witness[] { new Witness() { VerificationScript = new byte[] { 0x07 } } }, }; - var block = new Block() + var block = new TrimmedBlock() { Index = 0, Timestamp = 2, @@ -50,15 +51,14 @@ public void System_Blockchain_GetBlock() MerkleRoot = UInt256.Zero, NextConsensus = UInt160.Zero, ConsensusData = new ConsensusData() { Nonce = 1, PrimaryIndex = 1 }, - Transactions = new Transaction[] { tx } + Hashes = new UInt256[] { new ConsensusData() { Nonce = 1, PrimaryIndex = 1 }.Hash, tx.Hash } }; - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); using (var script = new ScriptBuilder()) { - script.EmitPush(block.Hash.ToArray()); - script.EmitSysCall(ApplicationEngine.System_Blockchain_GetBlock); + script.EmitDynamicCall(NativeContract.Ledger.Hash, "getBlock", block.Hash.ToArray()); // Without block @@ -71,13 +71,18 @@ public void System_Blockchain_GetBlock() // Not traceable block - var height = snapshot.BlockHashIndex.GetAndChange(); + const byte Prefix_Transaction = 11; + const byte Prefix_CurrentBlock = 12; + + var height = snapshot[NativeContract.Ledger.CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable(); height.Index = block.Index + ProtocolSettings.Default.MaxTraceableBlocks; - var blocks = snapshot.Blocks; - var txs = snapshot.Transactions; - blocks.Add(block.Hash, block.Trim()); - txs.Add(tx.Hash, new TransactionState() { Transaction = tx, BlockIndex = block.Index, VMState = VMState.HALT }); + UT_SmartContractHelper.BlocksAdd(snapshot, block.Hash, block); + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Transaction, tx.Hash), new StorageItem(new TransactionState + { + BlockIndex = block.Index, + Transaction = tx + }, true)); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(script.ToArray()); @@ -98,11 +103,6 @@ public void System_Blockchain_GetBlock() var array = engine.ResultStack.Pop(); Assert.AreEqual(block.Hash, new UInt256(array[0].GetSpan())); - - // Clean - - blocks.Delete(block.Hash); - txs.Delete(tx.Hash); } } @@ -316,7 +316,7 @@ public void System_Runtime_GasLeft() public void System_Runtime_GetInvocationCounter() { ContractState contractA, contractB, contractC; - var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // Create dummy contracts diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index e85b2e653c..76646ef3f9 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -55,13 +55,16 @@ public static ContractManifest CreateManifest(string method, ContractParameterTy return manifest; } - public static StorageKey CreateStorageKey(this NativeContract contract, byte prefix, ISerializable key) + public static StorageKey CreateStorageKey(this NativeContract contract, byte prefix, ISerializable key = null) { - return new StorageKey - { - Id = contract.Id, - Key = key.ToArray().Prepend(prefix).ToArray() - }; + var k = new KeyBuilder(contract.Id, prefix); + if (key != null) k = k.Add(key); + return k; + } + + public static StorageKey CreateStorageKey(this NativeContract contract, byte prefix, uint value) + { + return new KeyBuilder(contract.Id, prefix).AddBigEndian(value); } public static byte[] GetByteArray(int length, byte firstByte) diff --git a/tests/neo.UnitTests/TestVerifiable.cs b/tests/neo.UnitTests/TestVerifiable.cs index 7959eeec3b..5f98f07f2a 100644 --- a/tests/neo.UnitTests/TestVerifiable.cs +++ b/tests/neo.UnitTests/TestVerifiable.cs @@ -27,7 +27,7 @@ public void DeserializeUnsigned(BinaryReader reader) throw new NotImplementedException(); } - public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) + public UInt160[] GetScriptHashesForVerifying(DataCache snapshot) { throw new NotImplementedException(); } diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs index 22bcdaa26d..b10747df8d 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/neo.UnitTests/UT_DataCache.cs @@ -1,6 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; using Neo.Ledger; +using Neo.Persistence; +using Neo.SmartContract; using System.Linq; namespace Neo.UnitTests @@ -18,8 +20,8 @@ public void TestSetup() public void TestCachedFind_Between() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var storages = snapshot.Storages.CreateSnapshot(); - var cache = new CloneCache(storages); + var storages = snapshot.CreateSnapshot(); + var cache = new ClonedCache(storages); storages.Add ( @@ -57,8 +59,8 @@ public void TestCachedFind_Between() public void TestCachedFind_Last() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var storages = snapshot.Storages.CreateSnapshot(); - var cache = new CloneCache(storages); + var storages = snapshot.CreateSnapshot(); + var cache = new ClonedCache(storages); storages.Add ( @@ -89,8 +91,8 @@ public void TestCachedFind_Last() public void TestCachedFind_Empty() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var storages = snapshot.Storages.CreateSnapshot(); - var cache = new CloneCache(storages); + var storages = snapshot.CreateSnapshot(); + var cache = new ClonedCache(storages); cache.Add ( diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index c012d4917a..a903fead4a 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -158,7 +158,7 @@ public void TestMakeScript() { byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - Assert.AreEqual("0c14000000000000000000000000000000000000000011c01f0c0962616c616e63654f660c143d2f0bce7a4d9bb50b95425a71f444eb3045847541627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c01f0c0962616c616e63654f660c1428b3adab7269f9c2181db3cb741ebf551930e27041627d5b52", testScript.ToHexString()); } diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index da87f3148c..219bd62d25 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -202,13 +202,13 @@ public void TestGetAvailable() // Fake balance var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); wallet.GetAvailable(NativeContract.GAS.Hash).Should().Be(new BigDecimal(1000000000000, 8)); - entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -224,14 +224,14 @@ public void TestGetBalance() // Fake balance var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); wallet.GetBalance(UInt160.Zero, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(0, 0)); wallet.GetBalance(NativeContract.GAS.Hash, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(1000000000000, 8)); - entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -327,11 +327,11 @@ public void TestMakeTransaction1() // Fake balance var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); - var entry1 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry1 = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry1.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; key = NativeContract.NEO.CreateStorageKey(20, account.ScriptHash); - var entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoToken.NeoAccountState())); + var entry2 = snapshot.GetAndChange(key, () => new StorageItem(new NeoToken.NeoAccountState())); entry2.GetInteroperable().Balance = 10000 * NativeContract.NEO.Factor; snapshot.Commit(); @@ -359,8 +359,8 @@ public void TestMakeTransaction1() }); tx.Should().NotBeNull(); - entry1 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry1 = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); + entry2 = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry1.GetInteroperable().Balance = 0; entry2.GetInteroperable().Balance = 0; snapshot.Commit(); @@ -380,7 +380,7 @@ public void TestMakeTransaction2() // Fake balance var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 1000000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -395,7 +395,7 @@ public void TestMakeTransaction2() tx = wallet.MakeTransaction(new byte[] { }, null, null, Array.Empty()); tx.Should().NotBeNull(); - entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; snapshot.Commit(); } From 62b7cb02359beaca8dc08772a5f22eb08e1017ab Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 21 Jan 2021 15:10:27 +0100 Subject: [PATCH 49/65] Add decimals to BigDecimal constructor (#2255) * Add decimals * Optimize Co-authored-by: Erik Zhang --- src/neo/BigDecimal.cs | 29 +++++++++++++++++-- src/neo/Wallets/Wallet.cs | 2 +- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 3 +- .../Network/P2P/Payloads/UT_Transaction.cs | 24 +++++++-------- tests/neo.UnitTests/UT_BigDecimal.cs | 14 ++++----- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 17 ++++++----- 6 files changed, 57 insertions(+), 32 deletions(-) diff --git a/src/neo/BigDecimal.cs b/src/neo/BigDecimal.cs index ad1948f6bc..45e2ae770c 100644 --- a/src/neo/BigDecimal.cs +++ b/src/neo/BigDecimal.cs @@ -12,16 +12,39 @@ public struct BigDecimal public byte Decimals => decimals; public int Sign => value.Sign; + /// + /// Create BigDecimal from BigInteger + /// + /// Value + /// Decimals public BigDecimal(BigInteger value, byte decimals) { this.value = value; this.decimals = decimals; } - public BigDecimal(decimal value) + /// + /// Create BigDecimal from decimal + /// + /// Value + public unsafe BigDecimal(decimal value) { - this.decimals = BitConverter.GetBytes(decimal.GetBits(value)[3])[2]; - this.value = new BigInteger(decimal.Multiply((Decimal)Math.Pow(10, this.decimals), value)); + ReadOnlySpan buffer = new ReadOnlySpan(&value, sizeof(decimal)); + this.decimals = buffer[14]; + this.value = new BigInteger(decimal.Multiply((decimal)Math.Pow(10, decimals), value)); + } + + /// + /// Create BigDecimal from decimal + /// + /// Value + /// Decimals + public unsafe BigDecimal(decimal value, byte decimals) + { + ReadOnlySpan buffer = new ReadOnlySpan(&value, sizeof(decimal)); + if (buffer[14] > decimals) throw new ArgumentException(null, nameof(value)); + this.value = new BigInteger(decimal.Multiply((decimal)Math.Pow(10, decimals), value)); + this.decimals = decimals; } public BigDecimal ChangeDecimals(byte decimals) diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index d5c9ef0a13..a0dc5c4945 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -146,7 +146,7 @@ public BigDecimal GetBalance(UInt160 asset_id, params UInt160[] accounts) } using ApplicationEngine engine = ApplicationEngine.Run(script, gas: 20000000L * accounts.Length); if (engine.State.HasFlag(VMState.FAULT)) - return new BigDecimal(0, 0); + return new BigDecimal(BigInteger.Zero, 0); byte decimals = (byte)engine.ResultStack.Pop().GetInteger(); BigInteger amount = engine.ResultStack.Pop().GetInteger(); return new BigDecimal(amount, decimals); diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index c2051f40d4..40ba38c868 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -11,6 +11,7 @@ using Neo.Wallets.NEP6; using System; using System.Linq; +using System.Numerics; using System.Reflection; namespace Neo.UnitTests.Ledger @@ -119,7 +120,7 @@ private Transaction CreateValidTx(NEP6Wallet wallet, UInt160 account, uint nonce { AssetId = NativeContract.GAS.Hash, ScriptHash = account, - Value = new BigDecimal(1,8) + Value = new BigDecimal(BigInteger.One,8) } }, account); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index bd949063d3..77d4d5f750 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -139,7 +139,7 @@ public void FeeIsMultiSigContract() { AssetId = NativeContract.GAS.Hash, ScriptHash = acc.ScriptHash, - Value = new BigDecimal(1,8) + Value = new BigDecimal(BigInteger.One,8) } }, acc.ScriptHash); @@ -210,7 +210,7 @@ public void FeeIsSignatureContractDetailed() { AssetId = NativeContract.GAS.Hash, ScriptHash = acc.ScriptHash, - Value = new BigDecimal(1,8) + Value = new BigDecimal(BigInteger.One,8) } }, acc.ScriptHash); @@ -318,7 +318,7 @@ public void FeeIsSignatureContract_TestScope_Global() using (ScriptBuilder sb = new ScriptBuilder()) { // self-transfer of 1e-8 GAS - BigInteger value = (new BigDecimal(1, 8)).Value; + BigInteger value = (new BigDecimal(BigInteger.One, 8)).Value; sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); @@ -404,7 +404,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() using (ScriptBuilder sb = new ScriptBuilder()) { // self-transfer of 1e-8 GAS - BigInteger value = (new BigDecimal(1, 8)).Value; + BigInteger value = (new BigDecimal(BigInteger.One, 8)).Value; sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); @@ -491,7 +491,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() using (ScriptBuilder sb = new ScriptBuilder()) { // self-transfer of 1e-8 GAS - System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; + System.Numerics.BigInteger value = (new BigDecimal(BigInteger.One, 8)).Value; sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); @@ -579,7 +579,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() using (ScriptBuilder sb = new ScriptBuilder()) { // self-transfer of 1e-8 GAS - System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; + System.Numerics.BigInteger value = (new BigDecimal(BigInteger.One, 8)).Value; sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); @@ -631,7 +631,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() using (ScriptBuilder sb = new ScriptBuilder()) { // self-transfer of 1e-8 GAS - BigInteger value = (new BigDecimal(1, 8)).Value; + BigInteger value = (new BigDecimal(BigInteger.One, 8)).Value; sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); @@ -721,7 +721,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() using (ScriptBuilder sb = new ScriptBuilder()) { // self-transfer of 1e-8 GAS - BigInteger value = (new BigDecimal(1, 8)).Value; + BigInteger value = (new BigDecimal(BigInteger.One, 8)).Value; sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); @@ -984,7 +984,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() using (ScriptBuilder sb = new ScriptBuilder()) { // self-transfer of 1e-8 GAS - BigInteger value = (new BigDecimal(1, 8)).Value; + BigInteger value = (new BigDecimal(BigInteger.One, 8)).Value; sb.EmitDynamicCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value, null); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); @@ -1128,7 +1128,7 @@ public void Test_VerifyStateIndependent() { AssetId = NativeContract.GAS.Hash, ScriptHash = acc.ScriptHash, - Value = new BigDecimal(1,8) + Value = new BigDecimal(BigInteger.One,8) } }, acc.ScriptHash); @@ -1201,7 +1201,7 @@ public void Test_VerifyStateDependent() { AssetId = NativeContract.GAS.Hash, ScriptHash = acc.ScriptHash, - Value = new BigDecimal(1,8) + Value = new BigDecimal(BigInteger.One,8) } }, acc.ScriptHash); @@ -1271,7 +1271,7 @@ public void Test_Verify() { AssetId = NativeContract.GAS.Hash, ScriptHash = acc.ScriptHash, - Value = new BigDecimal(1,8) + Value = new BigDecimal(BigInteger.One,8) } }, acc.ScriptHash); diff --git a/tests/neo.UnitTests/UT_BigDecimal.cs b/tests/neo.UnitTests/UT_BigDecimal.cs index ccd147427a..8ea5f05054 100644 --- a/tests/neo.UnitTests/UT_BigDecimal.cs +++ b/tests/neo.UnitTests/UT_BigDecimal.cs @@ -39,23 +39,23 @@ public void TestBigDecimalConstructor() value.Value.Should().Be(new BigInteger(-10)); value.Decimals.Should().Be(0); - value = new BigDecimal(123.456789M); + value = new BigDecimal(123.456789M, 6); value.Value.Should().Be(new BigInteger(123456789)); value.Decimals.Should().Be(6); - value = new BigDecimal(-123.45M); - value.Value.Should().Be(new BigInteger(-12345)); - value.Decimals.Should().Be(2); + value = new BigDecimal(-123.45M, 3); + value.Value.Should().Be(new BigInteger(-123450)); + value.Decimals.Should().Be(3); - value = new BigDecimal(123.45M); + value = new BigDecimal(123.45M, 2); value.Value.Should().Be(new BigInteger(12345)); value.Decimals.Should().Be(2); - value = new BigDecimal(123M); + value = new BigDecimal(123M, 0); value.Value.Should().Be(new BigInteger(123)); value.Decimals.Should().Be(0); - value = new BigDecimal(0M); + value = new BigDecimal(0M, 0); value.Value.Should().Be(new BigInteger(0)); value.Decimals.Should().Be(0); } diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 219bd62d25..75d38c25a1 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -9,6 +9,7 @@ using Neo.Wallets; using System; using System.Collections.Generic; +using System.Numerics; namespace Neo.UnitTests.Wallets { @@ -206,7 +207,7 @@ public void TestGetAvailable() entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); - wallet.GetAvailable(NativeContract.GAS.Hash).Should().Be(new BigDecimal(1000000000000, 8)); + wallet.GetAvailable(NativeContract.GAS.Hash).Should().Be(new BigDecimal(new BigInteger(1000000000000M), 8)); entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; @@ -228,8 +229,8 @@ public void TestGetBalance() entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); - wallet.GetBalance(UInt160.Zero, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(0, 0)); - wallet.GetBalance(NativeContract.GAS.Hash, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(1000000000000, 8)); + wallet.GetBalance(UInt160.Zero, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(BigInteger.Zero, 0)); + wallet.GetBalance(NativeContract.GAS.Hash, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(new BigInteger(1000000000000M), 8)); entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; @@ -294,7 +295,7 @@ public void TestMakeTransaction1() { AssetId = NativeContract.GAS.Hash, ScriptHash = account.ScriptHash, - Value = new BigDecimal(1,8), + Value = new BigDecimal(BigInteger.One,8), Data = "Dec 12th" } }, UInt160.Zero); @@ -306,7 +307,7 @@ public void TestMakeTransaction1() { AssetId = NativeContract.GAS.Hash, ScriptHash = account.ScriptHash, - Value = new BigDecimal(1,8), + Value = new BigDecimal(BigInteger.One,8), Data = "Dec 12th" } }, account.ScriptHash); @@ -318,7 +319,7 @@ public void TestMakeTransaction1() { AssetId = UInt160.Zero, ScriptHash = account.ScriptHash, - Value = new BigDecimal(1,8), + Value = new BigDecimal(BigInteger.One,8), Data = "Dec 12th" } }, account.ScriptHash); @@ -342,7 +343,7 @@ public void TestMakeTransaction1() { AssetId = NativeContract.GAS.Hash, ScriptHash = account.ScriptHash, - Value = new BigDecimal(1,8) + Value = new BigDecimal(BigInteger.One,8) } }); tx.Should().NotBeNull(); @@ -353,7 +354,7 @@ public void TestMakeTransaction1() { AssetId = NativeContract.NEO.Hash, ScriptHash = account.ScriptHash, - Value = new BigDecimal(1,8), + Value = new BigDecimal(BigInteger.One,8), Data = "Dec 12th" } }); From ba4896e9f558dbebc63d3dd62d9f87e9e23880be Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 22 Jan 2021 00:39:38 +0800 Subject: [PATCH 50/65] Change ContractManifest serialization (#2254) --- src/neo/SmartContract/ContractState.cs | 4 +- src/neo/SmartContract/Helper.cs | 8 ++ src/neo/SmartContract/Manifest/ContractAbi.cs | 20 +++- .../Manifest/ContractEventDescriptor.cs | 19 +++- .../SmartContract/Manifest/ContractGroup.cs | 19 ++-- .../Manifest/ContractManifest.cs | 107 ++++++++---------- .../Manifest/ContractMethodDescriptor.cs | 43 ++++--- .../Manifest/ContractParameterDefinition.cs | 19 ++-- .../Manifest/ContractPermission.cs | 30 ++++- .../Manifest/ContractPermissionDescriptor.cs | 16 +++ .../Manifest/UT_ContractGroup.cs | 4 +- .../Manifest/UT_ContractManifest.cs | 56 ++++----- 12 files changed, 199 insertions(+), 146 deletions(-) diff --git a/src/neo/SmartContract/ContractState.cs b/src/neo/SmartContract/ContractState.cs index 0d3495b035..f9b7fed5d8 100644 --- a/src/neo/SmartContract/ContractState.cs +++ b/src/neo/SmartContract/ContractState.cs @@ -25,7 +25,7 @@ void IInteroperable.FromStackItem(StackItem stackItem) UpdateCounter = (ushort)array[1].GetInteger(); Hash = new UInt160(array[2].GetSpan()); Nef = array[3].GetSpan().AsSerializable(); - Manifest = ContractManifest.Parse(array[4].GetSpan()); + Manifest = array[4].ToInteroperable(); } /// @@ -53,7 +53,7 @@ public JObject ToJson() public StackItem ToStackItem(ReferenceCounter referenceCounter) { - return new Array(referenceCounter, new StackItem[] { Id, (int)UpdateCounter, Hash.ToArray(), Nef.ToArray(), Manifest.ToString() }); + return new Array(referenceCounter, new StackItem[] { Id, (int)UpdateCounter, Hash.ToArray(), Nef.ToArray(), Manifest.ToStackItem(referenceCounter) }); } } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 5f0e076c4e..bf8a6eae75 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -5,6 +5,7 @@ using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; using Neo.VM; +using Neo.VM.Types; using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -143,6 +144,13 @@ public static bool IsStandardContract(this byte[] script) return script.IsSignatureContract() || script.IsMultiSigContract(); } + public static T ToInteroperable(this StackItem item) where T : IInteroperable, new() + { + T t = new T(); + t.FromStackItem(item); + return t; + } + public static UInt160 ToScriptHash(this byte[] script) { return new UInt160(Crypto.Hash160(script)); diff --git a/src/neo/SmartContract/Manifest/ContractAbi.cs b/src/neo/SmartContract/Manifest/ContractAbi.cs index 5067cf9552..b2d890b111 100644 --- a/src/neo/SmartContract/Manifest/ContractAbi.cs +++ b/src/neo/SmartContract/Manifest/ContractAbi.cs @@ -1,14 +1,17 @@ using Neo.IO.Json; +using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; using System.Linq; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Manifest { /// /// NeoContract ABI /// - public class ContractAbi + public class ContractAbi : IInteroperable { private IReadOnlyDictionary<(string, int), ContractMethodDescriptor> methodDictionary; @@ -22,12 +25,19 @@ public class ContractAbi /// public ContractEventDescriptor[] Events { get; set; } - public ContractAbi Clone() + void IInteroperable.FromStackItem(StackItem stackItem) { - return new ContractAbi + Struct @struct = (Struct)stackItem; + Methods = ((Array)@struct[0]).Select(p => p.ToInteroperable()).ToArray(); + Events = ((Array)@struct[1]).Select(p => p.ToInteroperable()).ToArray(); + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) { - Methods = Methods.Select(p => p.Clone()).ToArray(), - Events = Events.Select(p => p.Clone()).ToArray() + new Array(referenceCounter, Methods.Select(p => p.ToStackItem(referenceCounter))), + new Array(referenceCounter, Events.Select(p => p.ToStackItem(referenceCounter))), }; } diff --git a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs index 8be3806b03..ea5787332c 100644 --- a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -1,9 +1,11 @@ using Neo.IO.Json; +using Neo.VM; +using Neo.VM.Types; using System.Linq; namespace Neo.SmartContract.Manifest { - public class ContractEventDescriptor + public class ContractEventDescriptor : IInteroperable { /// /// Name is the name of the method, which can be any valid identifier. @@ -15,12 +17,19 @@ public class ContractEventDescriptor /// public ContractParameterDefinition[] Parameters { get; set; } - public ContractEventDescriptor Clone() + public virtual void FromStackItem(StackItem stackItem) { - return new ContractEventDescriptor + Struct @struct = (Struct)stackItem; + Name = @struct[0].GetString(); + Parameters = ((Array)@struct[1]).Select(p => p.ToInteroperable()).ToArray(); + } + + public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) { - Name = Name, - Parameters = Parameters.Select(p => p.Clone()).ToArray() + Name, + new Array(referenceCounter, Parameters.Select(p => p.ToStackItem(referenceCounter))) }; } diff --git a/src/neo/SmartContract/Manifest/ContractGroup.cs b/src/neo/SmartContract/Manifest/ContractGroup.cs index 95b3d7e82b..9d1caa5250 100644 --- a/src/neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/neo/SmartContract/Manifest/ContractGroup.cs @@ -2,6 +2,8 @@ using Neo.Cryptography.ECC; using Neo.IO; using Neo.IO.Json; +using Neo.VM; +using Neo.VM.Types; using System; namespace Neo.SmartContract.Manifest @@ -10,7 +12,7 @@ namespace Neo.SmartContract.Manifest /// A group represents a set of mutually trusted contracts. A contract will trust and allow any contract in the same group to invoke it, and the user interface will not give any warnings. /// A group is identified by a public key and must be accompanied by a signature for the contract hash to prove that the contract is indeed included in the group. /// - public class ContractGroup + public class ContractGroup : IInteroperable { /// /// Pubkey represents the public key of the group. @@ -22,13 +24,16 @@ public class ContractGroup /// public byte[] Signature { get; set; } - public ContractGroup Clone() + void IInteroperable.FromStackItem(StackItem stackItem) { - return new ContractGroup - { - PubKey = PubKey, - Signature = Signature - }; + Struct @struct = (Struct)stackItem; + PubKey = @struct[0].GetSpan().AsSerializable(); + Signature = @struct[1].GetSpan().ToArray(); + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) { PubKey.ToArray(), Signature }; } /// diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs index 1e2abb3fd0..f356639a1d 100644 --- a/src/neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/neo/SmartContract/Manifest/ContractManifest.cs @@ -1,8 +1,10 @@ using Neo.IO; using Neo.IO.Json; +using Neo.VM; +using Neo.VM.Types; using System; -using System.IO; using System.Linq; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Manifest { @@ -10,25 +12,13 @@ namespace Neo.SmartContract.Manifest /// When a smart contract is deployed, it must explicitly declare the features and permissions it will use. /// When it is running, it will be limited by its declared list of features and permissions, and cannot make any behavior beyond the scope of the list. /// - public class ContractManifest : ISerializable + public class ContractManifest : IInteroperable { /// /// Max length for a valid Contract Manifest /// public const int MaxLength = ushort.MaxValue; - /// - /// Serialized size - /// - public int Size - { - get - { - int size = Utility.StrictUTF8.GetByteCount(ToString()); - return IO.Helper.GetVarSize(size) + size; - } - } - /// /// Contract name /// @@ -65,6 +55,37 @@ public int Size /// public JObject Extra { get; set; } + void IInteroperable.FromStackItem(StackItem stackItem) + { + Struct @struct = (Struct)stackItem; + Name = @struct[0].GetString(); + Groups = ((Array)@struct[1]).Select(p => p.ToInteroperable()).ToArray(); + SupportedStandards = ((Array)@struct[2]).Select(p => p.GetString()).ToArray(); + Abi = @struct[3].ToInteroperable(); + Permissions = ((Array)@struct[4]).Select(p => p.ToInteroperable()).ToArray(); + Trusts = @struct[5] switch + { + Null => WildcardContainer.CreateWildcard(), + Array array => WildcardContainer.Create(array.Select(p => new UInt160(p.GetSpan())).ToArray()), + _ => throw new ArgumentException(null, nameof(stackItem)) + }; + Extra = JObject.Parse(@struct[6].GetSpan()); + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) + { + Name, + new Array(referenceCounter, Groups.Select(p => p.ToStackItem(referenceCounter))), + new Array(referenceCounter, SupportedStandards.Select(p => (StackItem)p)), + Abi.ToStackItem(referenceCounter), + new Array(referenceCounter, Permissions.Select(p => p.ToStackItem(referenceCounter))), + Trusts.IsWildcard ? StackItem.Null : new Array(referenceCounter, Trusts.Select(p => (StackItem)p.ToArray())), + Extra is null ? "null" : Extra.ToByteArray(false) + }; + } + /// /// Parse ContractManifest from json /// @@ -72,9 +93,16 @@ public int Size /// Return ContractManifest public static ContractManifest FromJson(JObject json) { - var manifest = new ContractManifest(); - manifest.DeserializeFromJson(json); - return manifest; + return new ContractManifest + { + Name = json["name"].AsString(), + Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson(u)).ToArray(), + SupportedStandards = ((JArray)json["supportedstandards"]).Select(u => u.AsString()).ToArray(), + Abi = ContractAbi.FromJson(json["abi"]), + Permissions = ((JArray)json["permissions"]).Select(u => ContractPermission.FromJson(u)).ToArray(), + Trusts = WildcardContainer.FromJson(json["trusts"], u => UInt160.Parse(u.AsString())), + Extra = json["extra"] + }; } /// @@ -103,51 +131,6 @@ public JObject ToJson() }; } - /// - /// Clone - /// - /// Return a copy of this object - public ContractManifest Clone() - { - return new ContractManifest - { - Name = Name, - Groups = Groups.Select(p => p.Clone()).ToArray(), - SupportedStandards = SupportedStandards[..], - Abi = Abi.Clone(), - Permissions = Permissions.Select(p => p.Clone()).ToArray(), - Trusts = Trusts, - Extra = Extra?.Clone() - }; - } - - /// - /// String representation - /// - /// Return json string - public override string ToString() => ToJson().ToString(); - - public void Serialize(BinaryWriter writer) - { - writer.WriteVarString(ToString()); - } - - public void Deserialize(BinaryReader reader) - { - DeserializeFromJson(JObject.Parse(reader.ReadVarString(MaxLength))); - } - - private void DeserializeFromJson(JObject json) - { - Name = json["name"].AsString(); - Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson(u)).ToArray(); - SupportedStandards = ((JArray)json["supportedstandards"]).Select(u => u.AsString()).ToArray(); - Abi = ContractAbi.FromJson(json["abi"]); - Permissions = ((JArray)json["permissions"]).Select(u => ContractPermission.FromJson(u)).ToArray(); - Trusts = WildcardContainer.FromJson(json["trusts"], u => UInt160.Parse(u.AsString())); - Extra = json["extra"]; - } - /// /// Return true if is valid /// diff --git a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs index 3ffbc544c6..3b7129a97b 100644 --- a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -1,4 +1,6 @@ using Neo.IO.Json; +using Neo.VM; +using Neo.VM.Types; using System; using System.Linq; @@ -6,36 +8,41 @@ namespace Neo.SmartContract.Manifest { public class ContractMethodDescriptor : ContractEventDescriptor { - private int _offset; + /// + /// Returntype indicates the return type of the method. It can be one of the following values: + /// Any, Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, Map, InteropInterface, Void. + /// + public ContractParameterType ReturnType { get; set; } + private int _offset; public int Offset { get => _offset; set => _offset = value >= 0 ? value : throw new FormatException(); } - /// - /// Returntype indicates the return type of the method. It can be one of the following values: - /// Any, Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, Map, InteropInterface, Void. - /// - public ContractParameterType ReturnType { get; set; } - /// /// Determine if it's safe to call this method /// If a method is marked as safe, the user interface will not give any warnings when it is called by any other contract. /// public bool Safe { get; set; } - public new ContractMethodDescriptor Clone() + public override void FromStackItem(StackItem stackItem) { - return new ContractMethodDescriptor - { - Name = Name, - Parameters = Parameters.Select(p => p.Clone()).ToArray(), - Offset = Offset, - ReturnType = ReturnType, - Safe = Safe - }; + base.FromStackItem(stackItem); + Struct @struct = (Struct)stackItem; + ReturnType = (ContractParameterType)(byte)@struct[2].GetInteger(); + Offset = (int)@struct[3].GetInteger(); + Safe = @struct[4].GetBoolean(); + } + + public override StackItem ToStackItem(ReferenceCounter referenceCounter) + { + Struct @struct = (Struct)base.ToStackItem(referenceCounter); + @struct.Add((byte)ReturnType); + @struct.Add(Offset); + @struct.Add(Safe); + return @struct; } /// @@ -49,8 +56,8 @@ public int Offset { Name = json["name"].AsString(), Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), - Offset = (int)json["offset"].AsNumber(), ReturnType = (ContractParameterType)Enum.Parse(typeof(ContractParameterType), json["returntype"].AsString()), + Offset = (int)json["offset"].AsNumber(), Safe = json["safe"].AsBoolean(), }; } @@ -58,8 +65,8 @@ public int Offset public override JObject ToJson() { var json = base.ToJson(); - json["offset"] = Offset; json["returntype"] = ReturnType.ToString(); + json["offset"] = Offset; json["safe"] = Safe; return json; } diff --git a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs index 2c1ec6bdac..71f4f2391f 100644 --- a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -1,9 +1,11 @@ using Neo.IO.Json; +using Neo.VM; +using Neo.VM.Types; using System; namespace Neo.SmartContract.Manifest { - public class ContractParameterDefinition + public class ContractParameterDefinition : IInteroperable { /// /// Name is the name of the parameter, which can be any valid identifier. @@ -16,13 +18,16 @@ public class ContractParameterDefinition /// public ContractParameterType Type { get; set; } - public ContractParameterDefinition Clone() + void IInteroperable.FromStackItem(StackItem stackItem) { - return new ContractParameterDefinition - { - Name = Name, - Type = Type - }; + Struct @struct = (Struct)stackItem; + Name = @struct[0].GetString(); + Type = (ContractParameterType)(byte)@struct[1].GetInteger(); + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) { Name, (byte)Type }; } /// diff --git a/src/neo/SmartContract/Manifest/ContractPermission.cs b/src/neo/SmartContract/Manifest/ContractPermission.cs index 646f6d476c..37658df284 100644 --- a/src/neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/neo/SmartContract/Manifest/ContractPermission.cs @@ -1,13 +1,17 @@ +using Neo.IO; using Neo.IO.Json; +using Neo.VM; +using Neo.VM.Types; using System; using System.Linq; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Manifest { /// /// The permissions field is an array containing a set of Permission objects. It describes which contracts may be invoked and which methods are called. /// - public class ContractPermission + public class ContractPermission : IInteroperable { /// /// The contract field indicates the contract to be invoked. It can be a hash of a contract, a public key of a group, or a wildcard *. @@ -27,12 +31,28 @@ public class ContractPermission Methods = WildcardContainer.CreateWildcard() }; - public ContractPermission Clone() + void IInteroperable.FromStackItem(StackItem stackItem) { - return new ContractPermission + Struct @struct = (Struct)stackItem; + Contract = @struct[0] switch + { + Null => ContractPermissionDescriptor.CreateWildcard(), + StackItem item => new ContractPermissionDescriptor(item.GetSpan()) + }; + Methods = @struct[1] switch + { + Null => WildcardContainer.CreateWildcard(), + Array array => WildcardContainer.Create(array.Select(p => p.GetString()).ToArray()), + _ => throw new ArgumentException(null, nameof(stackItem)) + }; + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) { - Contract = Contract, - Methods = Methods + Contract.IsWildcard ? StackItem.Null : Contract.IsHash ? Contract.Hash.ToArray() : Contract.Group.ToArray(), + Methods.IsWildcard ? StackItem.Null : new Array(referenceCounter, Methods.Select(p => (StackItem)p)), }; } diff --git a/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs b/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs index 909ae27e42..80e22d5674 100644 --- a/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs @@ -1,4 +1,5 @@ using Neo.Cryptography.ECC; +using Neo.IO; using Neo.IO.Json; using System; @@ -19,6 +20,21 @@ private ContractPermissionDescriptor(UInt160 hash, ECPoint group) this.Group = group; } + internal ContractPermissionDescriptor(ReadOnlySpan span) + { + switch (span.Length) + { + case UInt160.Length: + Hash = new UInt160(span); + break; + case 33: + Group = span.AsSerializable(); + break; + default: + throw new ArgumentException(null, nameof(span)); + } + } + public static ContractPermissionDescriptor Create(UInt160 hash) { return new ContractPermissionDescriptor(hash, null); diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs index bd5b635ba1..f1fdbe8af4 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; +using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.Wallets; using System; @@ -23,7 +24,8 @@ public void TestClone() Signature = new byte[20] }; - var clone = contractGroup.Clone(); + var clone = new ContractGroup(); + ((IInteroperable)clone).FromStackItem(contractGroup.ToStackItem(null)); Assert.AreEqual(clone.ToJson().ToString(), contractGroup.ToJson().ToString()); } diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index 5f9e8d4405..df448e2e8a 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -1,8 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.IO.Json; +using Neo.SmartContract; using Neo.SmartContract.Manifest; -using System.IO; namespace Neo.UnitTests.SmartContract.Manifest { @@ -15,8 +15,8 @@ public void ParseFromJson_Default() var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); - Assert.AreEqual(manifest.ToString(), json); - Assert.AreEqual(manifest.ToString(), TestUtils.CreateDefaultManifest().ToString()); + Assert.AreEqual(manifest.ToJson().ToString(), json); + Assert.AreEqual(manifest.ToJson().ToString(), TestUtils.CreateDefaultManifest().ToJson().ToString()); Assert.IsTrue(manifest.IsValid(UInt160.Zero)); } @@ -25,7 +25,7 @@ public void ParseFromJson_Permissions() { var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); - Assert.AreEqual(manifest.ToString(), json); + Assert.AreEqual(manifest.ToJson().ToString(), json); var check = TestUtils.CreateDefaultManifest(); check.Permissions = new[] @@ -36,7 +36,7 @@ public void ParseFromJson_Permissions() Methods = WildcardContainer.Create("method1", "method2") } }; - Assert.AreEqual(manifest.ToString(), check.ToString()); + Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); } [TestMethod] @@ -44,10 +44,10 @@ public void ParseFromJson_SafeMethods() { var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); - Assert.AreEqual(manifest.ToString(), json); + Assert.AreEqual(manifest.ToJson().ToString(), json); var check = TestUtils.CreateDefaultManifest(); - Assert.AreEqual(manifest.ToString(), check.ToString()); + Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); } [TestMethod] @@ -55,11 +55,11 @@ public void ParseFromJson_Trust() { var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""extra"":null}"; var manifest = ContractManifest.Parse(json); - Assert.AreEqual(manifest.ToString(), json); + Assert.AreEqual(manifest.ToJson().ToString(), json); var check = TestUtils.CreateDefaultManifest(); check.Trusts = WildcardContainer.Create(UInt160.Parse("0x0000000000000000000000000000000000000001")); - Assert.AreEqual(manifest.ToString(), check.ToString()); + Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); } [TestMethod] @@ -67,11 +67,11 @@ public void ParseFromJson_Groups() { var json = @"{""name"":""testManifest"",""groups"":[{""pubkey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); - Assert.AreEqual(manifest.ToString(), json); + Assert.AreEqual(manifest.ToJson().ToString(), json); var check = TestUtils.CreateDefaultManifest(); check.Groups = new ContractGroup[] { new ContractGroup() { PubKey = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), Signature = "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141".HexToBytes() } }; - Assert.AreEqual(manifest.ToString(), check.ToString()); + Assert.AreEqual(manifest.ToJson().ToString(), check.ToJson().ToString()); } [TestMethod] @@ -86,24 +86,21 @@ public void ParseFromJson_Extra() [TestMethod] public void TestDeserializeAndSerialize() { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - BinaryReader reader = new BinaryReader(stream); var expected = TestUtils.CreateDefaultManifest(); expected.Extra = JObject.Parse(@"{""a"":123}"); - expected.Serialize(writer); - stream.Seek(0, SeekOrigin.Begin); - var actual = TestUtils.CreateDefaultManifest(); - actual.Deserialize(reader); - Assert.AreEqual(expected.ToString(), actual.ToString()); + + var clone = new ContractManifest(); + ((IInteroperable)clone).FromStackItem(expected.ToStackItem(null)); + Assert.AreEqual(expected.Extra.ToString(), @"{""a"":123}"); - } + Assert.AreEqual(expected.ToString(), clone.ToString()); - [TestMethod] - public void TestGetSize() - { - var temp = TestUtils.CreateDefaultManifest(); - Assert.AreEqual(165, temp.Size); + expected.Extra = null; + clone = new ContractManifest(); + ((IInteroperable)clone).FromStackItem(expected.ToStackItem(null)); + + Assert.AreEqual(expected.Extra, clone.Extra); + Assert.AreEqual(expected.ToString(), clone.ToString()); } [TestMethod] @@ -112,14 +109,5 @@ public void TestGenerator() ContractManifest contractManifest = new ContractManifest(); Assert.IsNotNull(contractManifest); } - - [TestMethod] - public void TestClone() - { - var expected = TestUtils.CreateDefaultManifest(); - expected.Extra = JObject.Parse(@"{ ""a"":123}"); - var actual = expected.Clone(); - Assert.AreEqual(actual.ToString(), expected.ToString()); - } } } From 02fdcc62d5a13f5ff2e39a8468ac4cd6f463b884 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 22 Jan 2021 14:50:59 +0100 Subject: [PATCH 51/65] Unify committee checks (#2258) * Unify committee checks * Unify with exceptions --- src/neo/SmartContract/Native/NeoToken.cs | 5 +- .../SmartContract/Native/PolicyContract.cs | 34 +++---- .../SmartContract/Native/UT_NeoToken.cs | 5 +- .../SmartContract/Native/UT_PolicyContract.cs | 98 ++++++++++--------- 4 files changed, 67 insertions(+), 75 deletions(-) diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index e050e16877..dee56c1095 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -175,16 +175,15 @@ internal override void PostPersist(ApplicationEngine engine) } [ContractMethod(0_05000000, CallFlags.WriteStates)] - private bool SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) + private void SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) { if (gasPerBlock < 0 || gasPerBlock > 10 * GAS.Factor) throw new ArgumentOutOfRangeException(nameof(gasPerBlock)); - if (!CheckCommittee(engine)) return false; + if (!CheckCommittee(engine)) throw new InvalidOperationException(); uint index = engine.PersistingBlock.Index + 1; StorageItem entry = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(index), () => new StorageItem(gasPerBlock)); entry.Set(gasPerBlock); - return true; } [ContractMethod(0_01000000, CallFlags.ReadStates)] diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 0b5b7a20c3..dfe4f8d32d 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -82,69 +82,63 @@ public bool IsBlocked(DataCache snapshot, UInt160 account) } [ContractMethod(0_03000000, CallFlags.WriteStates)] - private bool SetMaxBlockSize(ApplicationEngine engine, uint value) + private void SetMaxBlockSize(ApplicationEngine engine, uint value) { if (value > Message.PayloadMaxSize) throw new ArgumentOutOfRangeException(nameof(value)); - if (!CheckCommittee(engine)) return false; + if (!CheckCommittee(engine)) throw new InvalidOperationException(); StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize), () => new StorageItem()); storage.Set(value); - return true; } [ContractMethod(0_03000000, CallFlags.WriteStates)] - private bool SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value) + private void SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value) { if (value > Block.MaxTransactionsPerBlock) throw new ArgumentOutOfRangeException(nameof(value)); - if (!CheckCommittee(engine)) return false; + if (!CheckCommittee(engine)) throw new InvalidOperationException(); StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock), () => new StorageItem()); storage.Set(value); - return true; } [ContractMethod(0_03000000, CallFlags.WriteStates)] - private bool SetMaxBlockSystemFee(ApplicationEngine engine, long value) + private void SetMaxBlockSystemFee(ApplicationEngine engine, long value) { if (value <= 4007600) throw new ArgumentOutOfRangeException(nameof(value)); - if (!CheckCommittee(engine)) return false; + if (!CheckCommittee(engine)) throw new InvalidOperationException(); StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSystemFee), () => new StorageItem()); storage.Set(value); - return true; } [ContractMethod(0_03000000, CallFlags.WriteStates)] - private bool SetFeePerByte(ApplicationEngine engine, long value) + private void SetFeePerByte(ApplicationEngine engine, long value) { if (value < 0 || value > 1_00000000) throw new ArgumentOutOfRangeException(nameof(value)); - if (!CheckCommittee(engine)) return false; + if (!CheckCommittee(engine)) throw new InvalidOperationException(); StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_FeePerByte), () => new StorageItem()); storage.Set(value); - return true; } [ContractMethod(0_03000000, CallFlags.WriteStates)] - private bool SetExecFeeFactor(ApplicationEngine engine, uint value) + private void SetExecFeeFactor(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxExecFeeFactor) throw new ArgumentOutOfRangeException(nameof(value)); - if (!CheckCommittee(engine)) return false; + if (!CheckCommittee(engine)) throw new InvalidOperationException(); StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_ExecFeeFactor), () => new StorageItem()); storage.Set(value); - return true; } [ContractMethod(0_03000000, CallFlags.WriteStates)] - private bool SetStoragePrice(ApplicationEngine engine, uint value) + private void SetStoragePrice(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxStoragePrice) throw new ArgumentOutOfRangeException(nameof(value)); - if (!CheckCommittee(engine)) return false; + if (!CheckCommittee(engine)) throw new InvalidOperationException(); StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_StoragePrice), () => new StorageItem()); storage.Set(value); - return true; } [ContractMethod(0_03000000, CallFlags.WriteStates)] private bool BlockAccount(ApplicationEngine engine, UInt160 account) { - if (!CheckCommittee(engine)) return false; + if (!CheckCommittee(engine)) throw new InvalidOperationException(); var key = CreateStorageKey(Prefix_BlockedAccount).Add(account); if (engine.Snapshot.Contains(key)) return false; @@ -156,7 +150,7 @@ private bool BlockAccount(ApplicationEngine engine, UInt160 account) [ContractMethod(0_03000000, CallFlags.WriteStates)] private bool UnblockAccount(ApplicationEngine engine, UInt160 account) { - if (!CheckCommittee(engine)) return false; + if (!CheckCommittee(engine)) throw new InvalidOperationException(); var key = CreateStorageKey(Prefix_BlockedAccount).Add(account); if (!engine.Snapshot.Contains(key)) return false; diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index dcd67d350b..63adec3fb4 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -954,10 +954,7 @@ internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(DataCa return (false, false); } - var result = engine.ResultStack.Pop(); - result.Should().BeOfType(typeof(VM.Types.Boolean)); - - return (((VM.Types.Boolean)result).GetBoolean(), true); + return (true, true); } internal static (bool State, bool Result) Check_Vote(DataCache snapshot, byte[] account, byte[] pubkey, bool signAccount, Block persistingBlock) diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index c84807442b..309707d005 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -62,12 +62,13 @@ public void Check_SetMaxBlockSize() // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), block, + Assert.ThrowsException(() => + { + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), block, "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + }); - ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); + var ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); ret.Should().BeOfType(); ret.GetInteger().Should().Be(1024 * 256); @@ -87,8 +88,7 @@ public void Check_SetMaxBlockSize() ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.Should().BeNull(); ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); ret.Should().BeOfType(); @@ -103,17 +103,17 @@ public void Check_SetMaxBlockSystemFee() // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), block, + Assert.ThrowsException(() => + { + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), block, "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = 1024 * (long)NativeContract.GAS.Factor }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + }); - ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); + var ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); ret.Should().BeOfType(); ret.GetInteger().Should().Be(9000 * (long)NativeContract.GAS.Factor); @@ -133,8 +133,7 @@ public void Check_SetMaxBlockSystemFee() ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = 1024 * (long)NativeContract.GAS.Factor }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.Should().BeNull(); ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); ret.Should().BeOfType(); @@ -152,12 +151,13 @@ public void Check_SetMaxTransactionsPerBlock() // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, + Assert.ThrowsException(() => + { + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + }); - ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); + var ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); ret.GetInteger().Should().Be(512); @@ -165,8 +165,7 @@ public void Check_SetMaxTransactionsPerBlock() ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(NativeContract.NEO.GetCommitteeAddress(snapshot)), block, "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.Should().BeNull(); ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); @@ -184,12 +183,13 @@ public void Check_SetFeePerByte() // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, + Assert.ThrowsException(() => + { + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "setFeePerByte", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + }); - ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); + var ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); ret.GetInteger().Should().Be(1000); @@ -197,8 +197,7 @@ public void Check_SetFeePerByte() UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setFeePerByte", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.Should().BeNull(); ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); @@ -217,12 +216,13 @@ public void Check_SetBaseExecFee() // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, + Assert.ThrowsException(() => + { + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "setExecFeeFactor", new ContractParameter(ContractParameterType.Integer) { Value = 50 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + }); - ret = NativeContract.Policy.Call(snapshot, "getExecFeeFactor"); + var ret = NativeContract.Policy.Call(snapshot, "getExecFeeFactor"); ret.Should().BeOfType(); ret.GetInteger().Should().Be(30); @@ -241,8 +241,7 @@ public void Check_SetBaseExecFee() // Proper set ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setExecFeeFactor", new ContractParameter(ContractParameterType.Integer) { Value = 50 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.Should().BeNull(); ret = NativeContract.Policy.Call(snapshot, "getExecFeeFactor"); ret.Should().BeOfType(); @@ -261,12 +260,13 @@ public void Check_SetStoragePrice() // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, + Assert.ThrowsException(() => + { + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "setStoragePrice", new ContractParameter(ContractParameterType.Integer) { Value = 100500 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + }); - ret = NativeContract.Policy.Call(snapshot, "getStoragePrice"); + var ret = NativeContract.Policy.Call(snapshot, "getStoragePrice"); ret.Should().BeOfType(); ret.GetInteger().Should().Be(100000); @@ -285,8 +285,7 @@ public void Check_SetStoragePrice() // Proper set ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setStoragePrice", new ContractParameter(ContractParameterType.Integer) { Value = 300300 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.Should().BeNull(); ret = NativeContract.Policy.Call(snapshot, "getStoragePrice"); ret.Should().BeOfType(); @@ -304,16 +303,17 @@ public void Check_BlockAccount() // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), block, + Assert.ThrowsException(() => + { + NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), block, "blockAccount", new ContractParameter(ContractParameterType.ByteArray) { Value = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01").ToArray() }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + }); // With signature UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "blockAccount", new ContractParameter(ContractParameterType.ByteArray) { Value = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01").ToArray() }); ret.Should().BeOfType(); @@ -353,16 +353,17 @@ public void Check_Block_UnblockAccount() // Block without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, + Assert.ThrowsException(() => + { + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "blockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + }); NativeContract.Policy.IsBlocked(snapshot, UInt160.Zero).Should().BeFalse(); // Block with signature - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, + var ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "blockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeTrue(); @@ -371,10 +372,11 @@ public void Check_Block_UnblockAccount() // Unblock without signature - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, + Assert.ThrowsException(() => + { + ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, "unblockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + }); NativeContract.Policy.IsBlocked(snapshot, UInt160.Zero).Should().BeTrue(); From 6590c49634234ddfa30411add1cdf987fb6f4322 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 25 Jan 2021 21:50:47 +0800 Subject: [PATCH 52/65] Check ip format (#2262) --- src/neo/SmartContract/Native/NameService.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index 0c4463157d..1afd544cdc 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -27,6 +27,8 @@ public sealed class NameService : NonfungibleToken private const uint OneYear = 365 * 24 * 3600; private static readonly Regex rootRegex = new Regex("^[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); private static readonly Regex nameRegex = new Regex("^(?=.{3,255}$)([a-z0-9]{1,62}\\.)+[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); + private static readonly Regex ipv4Regex = new Regex("^(2(5[0-5]|[0-4]\\d))|1?\\d{1,2}(\\.((2(5[0-5]|[0-4]\\d))|1?\\d{1,2})){3}$", RegexOptions.Singleline); + private static readonly Regex ipv6Regex = new Regex("^([a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$", RegexOptions.Singleline | RegexOptions.IgnoreCase); internal NameService() { @@ -169,6 +171,7 @@ private void SetRecord(ApplicationEngine engine, string name, RecordType type, s switch (type) { case RecordType.A: + if (!ipv4Regex.IsMatch(data)) throw new FormatException(); if (!IPAddress.TryParse(data, out IPAddress address)) throw new FormatException(); if (address.AddressFamily != AddressFamily.InterNetwork) throw new FormatException(); break; @@ -179,6 +182,7 @@ private void SetRecord(ApplicationEngine engine, string name, RecordType type, s if (Utility.StrictUTF8.GetByteCount(data) > 255) throw new FormatException(); break; case RecordType.AAAA: + if (!ipv6Regex.IsMatch(data)) throw new FormatException(); if (!IPAddress.TryParse(data, out address)) throw new FormatException(); if (address.AddressFamily != AddressFamily.InterNetworkV6) throw new FormatException(); break; From aaba97844a62d8c01ca48826f0fd3167199e184c Mon Sep 17 00:00:00 2001 From: cloud8little <34291844+cloud8little@users.noreply.github.com> Date: Mon, 25 Jan 2021 22:38:39 +0800 Subject: [PATCH 53/65] Add NameService UT (#2261) --- .../SmartContract/Native/UT_NNS.cs | 698 ++++++++++++++++++ 1 file changed, 698 insertions(+) create mode 100644 tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs new file mode 100644 index 0000000000..2f2ba3e5c1 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs @@ -0,0 +1,698 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.UnitTests.Extensions; +using Neo.VM; +using Neo.Wallets; +using System; +using System.Linq; +using System.Numerics; + +namespace Neo.UnitTests.SmartContract.Native +{ + [TestClass] + public class UT_NNS + { + private DataCache _snapshot; + private Block _persistingBlock; + + private const byte Prefix_Roots = 10; + private const byte Prefix_DomainPrice = 22; + private const byte Prefix_Expiration = 20; + private const byte Prefix_Record = 12; + + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + _snapshot = Blockchain.Singleton.GetSnapshot(); + _persistingBlock = new Block() { Index = 0, Transactions = Array.Empty(), ConsensusData = new ConsensusData() }; + } + + [TestMethod] + public void Check_Name() => NativeContract.NameService.Name.Should().Be(nameof(NameService)); + + [TestMethod] + public void Check_Symbol() => NativeContract.NameService.Symbol(_snapshot).Should().Be("NNS"); + + [TestMethod] + public void Test_SetRecord_IPV4() + { + var snapshot = _snapshot.CreateSnapshot(); + // Fake blockchain + + var persistingBlock = new Block() { Index = 1000 }; + UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // committee member,add a new root and then register, setrecord + string validroot = "testroot"; + var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + + string name = "testname"; + string domain = name + "." + validroot; + + //before register + var checkAvail_ret = Check_IsAvailable(snapshot, UInt160.Zero, domain, persistingBlock); + checkAvail_ret.Result.Should().BeTrue(); + checkAvail_ret.State.Should().BeTrue(); + + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + var register_ret = Check_Register(snapshot, domain, from, persistingBlock); + register_ret.Result.Should().BeTrue(); + register_ret.State.Should().BeTrue(); + + //check NFT token + Assert.AreEqual(NativeContract.NameService.BalanceOf(snapshot, from), (BigInteger)1); + + //after register + checkAvail_ret = Check_IsAvailable(snapshot, UInt160.Zero, domain, persistingBlock); + checkAvail_ret.Result.Should().BeTrue(); + checkAvail_ret.State.Should().BeFalse(); + + //set As IPv4 Address + string testA = "10.10.10.10"; + var setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testA, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + var getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.A, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testA); + + testA = "0.0.0.0"; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testA, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.A, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testA); + + testA = "255.255.255.255"; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testA, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.A, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testA); + + //invalid case + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "1a", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "256.0.0.0", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.0.0.-1", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.0.0.0.1", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "11111111.11111111.11111111.11111111", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "11111111.11111111.11111111.11111111", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "ff.ff.ff.ff", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.0.256", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.0.0", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.257", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "1.1", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "257", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "1", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + } + + [TestMethod] + public void Test_SetRecord_CNAME() + { + var snapshot = _snapshot.CreateSnapshot(); + // Fake blockchain + + var persistingBlock = new Block() { Index = 1000 }; + UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // committee member,add a new root and then register, setrecord + string validroot = "testroot"; + var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + + string name = "testname"; + + string domain = name + "." + validroot; + + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + var register_ret = Check_Register(snapshot, domain, from, persistingBlock); + register_ret.Result.Should().BeTrue(); + register_ret.State.Should().BeTrue(); + + //set as CNAME + string testCName = "a1.b1"; + var setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, testCName, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + var getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.CNAME, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testCName); + + testCName = "a1.b1.c1"; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, testCName, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.CNAME, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testCName); + + testCName = "1a.b1"; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, testCName, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.CNAME, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testCName); + + //invalid case + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, "a1", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, "a1.", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, "..", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, "\n.", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, "", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, " ", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, "A1.b1", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, "a1.B1", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.CNAME, "1a.1b", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + } + + [TestMethod] + public void Test_SetRecord_TxT() + { + var snapshot = _snapshot.CreateSnapshot(); + // Fake blockchain + + var persistingBlock = new Block() { Index = 1000 }; + UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // committee member,add a new root and then register, setrecord + string validroot = "testroot"; + var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + + string name = "testname"; + string domain = name + "." + validroot; + + //before register + var checkAvail_ret = Check_IsAvailable(snapshot, UInt160.Zero, domain, persistingBlock); + checkAvail_ret.Result.Should().BeTrue(); + checkAvail_ret.State.Should().BeTrue(); + + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + var register_ret = Check_Register(snapshot, domain, from, persistingBlock); + register_ret.Result.Should().BeTrue(); + register_ret.State.Should().BeTrue(); + + //set as txt + string testTxt = "testtxt"; + var setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.TXT, testTxt, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + var getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.TXT, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testTxt); + + testTxt = "."; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.TXT, testTxt, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.TXT, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testTxt); + + testTxt = "\n"; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.TXT, testTxt, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.TXT, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testTxt); + + testTxt = "10.10.10.10"; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.TXT, testTxt, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.TXT, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testTxt); + + testTxt = "2001:0000:1F1F:0000:0000:0100:11A0:ADDF"; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.TXT, testTxt, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.TXT, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testTxt); + + testTxt = ""; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.TXT, testTxt, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.TXT, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testTxt); + + testTxt = " "; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.TXT, testTxt, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.TXT, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testTxt); + + //invalid case + + testTxt = "a"; + for (int i = 0; i < 8; i++) testTxt = testTxt + testTxt; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.TXT, testTxt, from, persistingBlock); + setRecord_ret.Should().BeFalse(); + } + + [TestMethod] + public void Test_SetRecord_IPV6() + { + var snapshot = _snapshot.CreateSnapshot(); + // Fake blockchain + + var persistingBlock = new Block() { Index = 1000 }; + UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // committee member,add a new root and then register, setrecord + string validroot = "testroot"; + var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + + string name = "testname"; + string domain = name + "." + validroot; + + //before register + var checkAvail_ret = Check_IsAvailable(snapshot, UInt160.Zero, domain, persistingBlock); + checkAvail_ret.Result.Should().BeTrue(); + checkAvail_ret.State.Should().BeTrue(); + + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + var register_ret = Check_Register(snapshot, domain, from, persistingBlock); + register_ret.Result.Should().BeTrue(); + register_ret.State.Should().BeTrue(); + + //set as IPV6 address + string testAAAA = "2001:0000:1F1F:0000:0000:0100:11A0:ADDF"; + var setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, testAAAA, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + var getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.AAAA, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testAAAA); + + testAAAA = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, testAAAA, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.AAAA, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testAAAA); + + //invalid case + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, "10.10.10.10", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, "", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, "\n", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, ": : : : : : :", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffg", from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + } + + + [TestMethod] + public void Test_AddRootValid() + { + var snapshot = _snapshot.CreateSnapshot(); + var persistingBlock = _persistingBlock; + + UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); + + var ret = Check_AddRoot(snapshot, committeeAddress, "a", persistingBlock); + ret.Should().BeTrue(); + + ret = Check_AddRoot(snapshot, committeeAddress, "a1", persistingBlock); + ret.Should().BeTrue(); + + ret = Check_AddRoot(snapshot, committeeAddress, "aw", persistingBlock); + ret.Should().BeTrue(); + + ret = Check_AddRoot(snapshot, committeeAddress, "a123456789123456", persistingBlock); + ret.Should().BeTrue(); + + ret = Check_AddRoot(snapshot, committeeAddress, "abcdefg", persistingBlock); + ret.Should().BeTrue(); + + } + + [TestMethod] + public void Test_AddRootInvalid() + { + var snapshot = _snapshot.CreateSnapshot(); + var persistingBlock = _persistingBlock; + //non-committee member + string validroot = "testroot"; + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + var ret = Check_AddRoot(snapshot, new UInt160(from), validroot, persistingBlock); + ret.Should().BeFalse(); + + UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // committee member,add a existing root + ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + ret.Should().BeTrue(); + + ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + ret.Should().BeFalse(); + + //invalid root string + ret = Check_AddRoot(snapshot, committeeAddress, "", persistingBlock); + ret.Should().BeFalse(); + + ret = Check_AddRoot(snapshot, committeeAddress, "\n", persistingBlock); + ret.Should().BeFalse(); + + ret = Check_AddRoot(snapshot, committeeAddress, ".", persistingBlock); + ret.Should().BeFalse(); + + //first character is not a-z + ret = Check_AddRoot(snapshot, committeeAddress, "1a", persistingBlock); + ret.Should().BeFalse(); + + ret = Check_AddRoot(snapshot, committeeAddress, "A1", persistingBlock); + ret.Should().BeFalse(); + + ret = Check_AddRoot(snapshot, committeeAddress, "a1-2", persistingBlock); + ret.Should().BeFalse(); + + ret = Check_AddRoot(snapshot, committeeAddress, "a1234567891234567", persistingBlock); + ret.Should().BeFalse(); + + } + + [TestMethod] + public void Test_SetAdmin() + { + var snapshot = _snapshot.CreateSnapshot(); + // Fake blockchain + + var persistingBlock = new Block() { Index = 1000 }; + UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); + + // committee member,add a new root and then register, setrecord + string validroot = "testroot"; + var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + + string name = "testname"; + string domain = name + "." + validroot; + + //before register + var checkAvail_ret = Check_IsAvailable(snapshot, UInt160.Zero, domain, persistingBlock); + checkAvail_ret.Result.Should().BeTrue(); + checkAvail_ret.State.Should().BeTrue(); + + byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + var register_ret = Check_Register(snapshot, domain, from, persistingBlock); + register_ret.Result.Should().BeTrue(); + register_ret.State.Should().BeTrue(); + + //check NFT token + Assert.AreEqual(NativeContract.NameService.BalanceOf(snapshot, from), (BigInteger)1); + + //after register + checkAvail_ret = Check_IsAvailable(snapshot, UInt160.Zero, domain, persistingBlock); + checkAvail_ret.Result.Should().BeTrue(); + checkAvail_ret.State.Should().BeFalse(); + + UInt160 admin = Contract.CreateSignatureContract(ECCurve.Secp256r1.G).ScriptHash; + var checkadmin_ret = Check_SetAdmin(snapshot, domain, admin, from, persistingBlock); + checkadmin_ret.Should().BeTrue(); + + //setrecord with admin account + string testA = "10.10.10.10"; + var setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testA, admin.ToArray(), persistingBlock); + setRecord_ret.Should().BeTrue(); + + var getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.A, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testA); + + //transfer the NFT token, admin set to null + byte[] to = UInt160.Zero.ToArray(); + var transfer_ret = Check_Transfer(snapshot, UInt160.Zero, domain, new UInt160(from), persistingBlock); + transfer_ret.Should().BeTrue(); + Assert.AreEqual(NativeContract.NameService.BalanceOf(snapshot, from), (BigInteger)0); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testA, admin.ToArray(), persistingBlock); + setRecord_ret.Should().BeFalse(); + + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testA, from, persistingBlock); + setRecord_ret.Should().BeFalse(); + + checkadmin_ret = Check_SetAdmin(snapshot, domain, admin, from, persistingBlock); + checkadmin_ret.Should().BeFalse(); + } + + internal static bool Check_AddRoot(DataCache snapshot, UInt160 account, string root, Block persistingBlock) + { + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(account), snapshot, persistingBlock); + engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); + + var script = new ScriptBuilder(); + script.EmitPush(root); + script.EmitPush("addRoot"); + engine.LoadScript(script.ToArray()); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + return true; + } + + internal static bool Check_Transfer(DataCache snapshot, UInt160 to, string domain, UInt160 owner, Block persistingBlock) + { + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock); + engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); + + var script = new ScriptBuilder(); + script.EmitPush(domain); + script.EmitPush(to); + script.EmitPush("transfer"); + engine.LoadScript(script.ToArray()); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + return true; + } + + internal static (bool State, bool Result) Check_IsAvailable(DataCache snapshot, UInt160 account, string name, Block persistingBlock) + { + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(account), snapshot, persistingBlock); + + engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); + + var script = new ScriptBuilder(); + script.EmitPush(name); + script.EmitPush("isAvailable"); + engine.LoadScript(script.ToArray()); + + if (engine.Execute() == VMState.FAULT) + { + return (false, false); + } + + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + + return (((VM.Types.Boolean)result).GetBoolean(), true); + } + + internal static (bool State, bool Result) Check_SetPrice(DataCache snapshot, byte[] pubkey, long price, Block persistingBlock) + { + using var engine = ApplicationEngine.Create(TriggerType.Application, + new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); + + engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); + + using var script = new ScriptBuilder(); + script.EmitPush(price); + script.EmitPush("setPrice"); + engine.LoadScript(script.ToArray()); + + if (engine.Execute() == VMState.FAULT) + { + return (false, false); + } + + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + + return (true, result.GetBoolean()); + } + + internal static BigDecimal Check_GetPrice(DataCache snapshot, Block persistingBlock) + { + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); + + engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); + + using var script = new ScriptBuilder(); + script.EmitPush("getPrice"); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(long)); + + return new BigDecimal(((VM.Types.PrimitiveType)result).GetInteger(), NativeContract.GAS.Decimals); + } + + internal static (bool State, bool Result) Check_Register(DataCache snapshot, string name, byte[] owner, Block persistingBlock) + { + using var engine = ApplicationEngine.Create(TriggerType.Application, + new Nep17NativeContractExtensions.ManualWitness(new UInt160(owner)), snapshot, persistingBlock); + + engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); + + using var script = new ScriptBuilder(); + script.EmitPush(new UInt160(owner)); + script.EmitPush(name); + script.EmitPush("register"); + engine.LoadScript(script.ToArray()); + + if (engine.Execute() == VMState.FAULT) + { + return (false, false); + } + + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + + return (true, result.GetBoolean()); + } + + internal static bool Check_SetRecord(DataCache snapshot, string name, RecordType type, string data, byte[] pubkey, Block persistingBlock) + { + using var engine = ApplicationEngine.Create(TriggerType.Application, + new Nep17NativeContractExtensions.ManualWitness(new UInt160(pubkey)), snapshot, persistingBlock); + + engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); + + using var script = new ScriptBuilder(); + script.EmitPush(data); + script.EmitPush(type); + script.EmitPush(name); + script.EmitPush("setRecord"); + engine.LoadScript(script.ToArray()); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + return true; + } + + internal static (bool State, string Result) Check_GetRecord(DataCache snapshot, string name, RecordType type, Block persistingBlock) + { + using var engine = ApplicationEngine.Create(TriggerType.Application, + new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), snapshot, persistingBlock); + + engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); + + using var script = new ScriptBuilder(); + script.EmitPush(type); + script.EmitPush(name); + script.EmitPush("getRecord"); + engine.LoadScript(script.ToArray()); + + if (engine.Execute() == VMState.FAULT) + { + return (false, null); + } + + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(Neo.VM.Types.ByteString)); + + return (true, result.ToString()); + } + + internal static bool Check_SetAdmin(DataCache snapshot, string name, UInt160 admin, byte[] pubkey, Block persistingBlock) + { + using var engine = ApplicationEngine.Create(TriggerType.Application, + new Nep17NativeContractExtensions.ManualWitness(new UInt160[] { admin, new UInt160(pubkey) }), snapshot, persistingBlock); + + engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); + + using var script = new ScriptBuilder(); + script.EmitPush(admin); + script.EmitPush(name); + script.EmitPush("setAdmin"); + engine.LoadScript(script.ToArray()); + + if (engine.Execute() == VMState.FAULT) + { + return false; + } + + return true; + } + } +} From 677bd9648d40b31076ba78c88c8a48bc0e919f66 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 26 Jan 2021 21:13:35 +0800 Subject: [PATCH 54/65] Add initial values for native contracts (#2265) * Add initial values for native contracts * GetRequest as BigEndian * Check endianness in GetRequests * Fix * Optimize Co-authored-by: Shargon --- src/neo/SmartContract/ApplicationEngine.cs | 4 +- .../Native/ContractManagement.cs | 3 +- src/neo/SmartContract/Native/NameService.cs | 14 ++--- .../SmartContract/Native/NonfungibleToken.cs | 10 ++-- .../SmartContract/Native/OracleContract.cs | 17 +++--- .../SmartContract/Native/PolicyContract.cs | 54 ++++++++----------- .../SmartContract/Native/UT_PolicyContract.cs | 7 +-- 7 files changed, 53 insertions(+), 56 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 936eaac7f6..c5857228da 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -59,8 +59,8 @@ protected ApplicationEngine(TriggerType trigger, IVerifiable container, DataCach this.Snapshot = snapshot; this.PersistingBlock = persistingBlock; this.gas_amount = gas; - this.exec_fee_factor = snapshot is null ? PolicyContract.DefaultExecFeeFactor : NativeContract.Policy.GetExecFeeFactor(Snapshot); - this.StoragePrice = snapshot is null ? PolicyContract.DefaultStoragePrice : NativeContract.Policy.GetStoragePrice(Snapshot); + this.exec_fee_factor = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultExecFeeFactor : NativeContract.Policy.GetExecFeeFactor(Snapshot); + this.StoragePrice = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultStoragePrice : NativeContract.Policy.GetStoragePrice(Snapshot); } protected internal void AddGas(long gas) diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 2bf9bd9231..416bfa594a 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -65,7 +65,7 @@ internal ContractManagement() private int GetNextAvailableId(DataCache snapshot) { - StorageItem item = snapshot.GetAndChange(CreateStorageKey(Prefix_NextAvailableId), () => new StorageItem(1)); + StorageItem item = snapshot.GetAndChange(CreateStorageKey(Prefix_NextAvailableId)); int value = (int)(BigInteger)item; item.Add(1); return value; @@ -74,6 +74,7 @@ private int GetNextAvailableId(DataCache snapshot) internal override void Initialize(ApplicationEngine engine) { engine.Snapshot.Add(CreateStorageKey(Prefix_MinimumDeploymentFee), new StorageItem(10_00000000)); + engine.Snapshot.Add(CreateStorageKey(Prefix_NextAvailableId), new StorageItem(0)); } internal override void OnPersist(ApplicationEngine engine) diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index 1afd544cdc..4e6bec1d36 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -36,6 +36,8 @@ internal NameService() internal override void Initialize(ApplicationEngine engine) { + base.Initialize(engine); + engine.Snapshot.Add(CreateStorageKey(Prefix_Roots), new StorageItem(new StringList())); engine.Snapshot.Add(CreateStorageKey(Prefix_DomainPrice), new StorageItem(10_00000000)); } @@ -68,7 +70,7 @@ private void AddRoot(ApplicationEngine engine, string root) { if (!rootRegex.IsMatch(root)) throw new ArgumentException(null, nameof(root)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - StringList roots = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Roots), () => new StorageItem(new StringList())).GetInteroperable(); + StringList roots = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Roots)).GetInteroperable(); int index = roots.BinarySearch(root); if (index >= 0) throw new InvalidOperationException("The name already exists."); roots.Insert(~index, root); @@ -76,7 +78,7 @@ private void AddRoot(ApplicationEngine engine, string root) public IEnumerable GetRoots(DataCache snapshot) { - return snapshot.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable() ?? Enumerable.Empty(); + return snapshot[CreateStorageKey(Prefix_Roots)].GetInteroperable(); } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -101,8 +103,8 @@ public bool IsAvailable(DataCache snapshot, string name) if (names.Length != 2) throw new ArgumentException(null, nameof(name)); byte[] hash = GetKey(Utility.StrictUTF8.GetBytes(name)); if (snapshot.TryGet(CreateStorageKey(Prefix_Token).Add(hash)) is not null) return false; - StringList roots = snapshot.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable(); - if (roots is null || roots.BinarySearch(names[1]) < 0) throw new InvalidOperationException(); + StringList roots = snapshot[CreateStorageKey(Prefix_Roots)].GetInteroperable(); + if (roots.BinarySearch(names[1]) < 0) throw new InvalidOperationException(); return true; } @@ -115,8 +117,8 @@ private bool Register(ApplicationEngine engine, string name, UInt160 owner) if (!engine.CheckWitnessInternal(owner)) throw new InvalidOperationException(); byte[] hash = GetKey(Utility.StrictUTF8.GetBytes(name)); if (engine.Snapshot.TryGet(CreateStorageKey(Prefix_Token).Add(hash)) is not null) return false; - StringList roots = engine.Snapshot.TryGet(CreateStorageKey(Prefix_Roots))?.GetInteroperable(); - if (roots is null || roots.BinarySearch(names[1]) < 0) throw new InvalidOperationException(); + StringList roots = engine.Snapshot[CreateStorageKey(Prefix_Roots)].GetInteroperable(); + if (roots.BinarySearch(names[1]) < 0) throw new InvalidOperationException(); engine.AddGas(GetPrice(engine.Snapshot)); NameState state = new NameState { diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index 8fa3696fe5..35f799a57f 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -62,12 +62,17 @@ protected NonfungibleToken() protected virtual byte[] GetKey(byte[] tokenId) => tokenId; + internal override void Initialize(ApplicationEngine engine) + { + engine.Snapshot.Add(CreateStorageKey(Prefix_TotalSupply), new StorageItem(BigInteger.Zero)); + } + protected void Mint(ApplicationEngine engine, TokenState token) { engine.Snapshot.Add(CreateStorageKey(Prefix_Token).Add(GetKey(token.Id)), new StorageItem(token)); NFTAccountState account = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Account).Add(token.Owner), () => new StorageItem(new NFTAccountState())).GetInteroperable(); account.Add(token.Id); - engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero)).Add(1); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply)).Add(1); PostTransfer(engine, null, token.Owner, token.Id); } @@ -93,8 +98,7 @@ private protected void Burn(ApplicationEngine engine, StorageKey key) [ContractMethod(0_01000000, CallFlags.ReadStates)] public BigInteger TotalSupply(DataCache snapshot) { - StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_TotalSupply)); - return storage ?? BigInteger.Zero; + return snapshot[CreateStorageKey(Prefix_TotalSupply)]; } [ContractMethod(0_01000000, CallFlags.ReadStates)] diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index 5e4e710de0..8bb656540c 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -8,6 +8,7 @@ using Neo.VM; using Neo.VM.Types; using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -104,12 +105,12 @@ private UInt256 GetOriginalTxid(ApplicationEngine engine) public OracleRequest GetRequest(DataCache snapshot, ulong id) { - return snapshot.TryGet(CreateStorageKey(Prefix_Request).Add(id))?.GetInteroperable(); + return snapshot.TryGet(CreateStorageKey(Prefix_Request).AddBigEndian(id))?.GetInteroperable(); } public IEnumerable<(ulong, OracleRequest)> GetRequests(DataCache snapshot) { - return snapshot.Find(new KeyBuilder(Id, Prefix_Request).ToArray()).Select(p => (BitConverter.ToUInt64(p.Key.Key, 1), p.Value.GetInteroperable())); + return snapshot.Find(CreateStorageKey(Prefix_Request).ToArray()).Select(p => (BinaryPrimitives.ReadUInt64BigEndian(p.Key.Key.AsSpan(1)), p.Value.GetInteroperable())); } public IEnumerable<(ulong, OracleRequest)> GetRequestsByUrl(DataCache snapshot, string url) @@ -117,7 +118,7 @@ public OracleRequest GetRequest(DataCache snapshot, ulong id) IdList list = snapshot.TryGet(CreateStorageKey(Prefix_IdList).Add(GetUrlHash(url)))?.GetInteroperable(); if (list is null) yield break; foreach (ulong id in list) - yield return (id, snapshot[CreateStorageKey(Prefix_Request).Add(id)].GetInteroperable()); + yield return (id, snapshot[CreateStorageKey(Prefix_Request).AddBigEndian(id)].GetInteroperable()); } private static byte[] GetUrlHash(string url) @@ -127,7 +128,7 @@ private static byte[] GetUrlHash(string url) internal override void Initialize(ApplicationEngine engine) { - engine.Snapshot.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BitConverter.GetBytes(0ul))); + engine.Snapshot.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BigInteger.Zero)); } internal override void PostPersist(ApplicationEngine engine) @@ -140,7 +141,7 @@ internal override void PostPersist(ApplicationEngine engine) if (response is null) continue; //Remove the request from storage - StorageKey key = CreateStorageKey(Prefix_Request).Add(response.Id); + StorageKey key = CreateStorageKey(Prefix_Request).AddBigEndian(response.Id); OracleRequest request = engine.Snapshot.TryGet(key)?.GetInteroperable(); if (request == null) continue; engine.Snapshot.Delete(key); @@ -184,13 +185,13 @@ private void Request(ApplicationEngine engine, string url, string filter, string //Increase the request id StorageItem item_id = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_RequestId)); - ulong id = BitConverter.ToUInt64(item_id.Value) + 1; - item_id.Value = BitConverter.GetBytes(id); + ulong id = (ulong)(BigInteger)item_id; + item_id.Add(1); //Put the request to storage if (ContractManagement.GetContract(engine.Snapshot, engine.CallingScriptHash) is null) throw new InvalidOperationException(); - engine.Snapshot.Add(CreateStorageKey(Prefix_Request).Add(item_id.Value), new StorageItem(new OracleRequest + engine.Snapshot.Add(CreateStorageKey(Prefix_Request).AddBigEndian(id), new StorageItem(new OracleRequest { OriginalTxid = GetOriginalTxid(engine), GasForResponse = gasForResponse, diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index dfe4f8d32d..eb60511042 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -27,52 +27,50 @@ internal PolicyContract() { } + internal override void Initialize(ApplicationEngine engine) + { + engine.Snapshot.Add(CreateStorageKey(Prefix_MaxTransactionsPerBlock), new StorageItem(512)); + engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(1000)); + engine.Snapshot.Add(CreateStorageKey(Prefix_MaxBlockSize), new StorageItem(1024 * 256)); + engine.Snapshot.Add(CreateStorageKey(Prefix_MaxBlockSystemFee), new StorageItem(9000 * GAS.Factor)); // For the transfer method of NEP5, the maximum persisting time is about three seconds. + engine.Snapshot.Add(CreateStorageKey(Prefix_ExecFeeFactor), new StorageItem(DefaultExecFeeFactor)); + engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice)); + } + [ContractMethod(0_01000000, CallFlags.ReadStates)] public uint GetMaxTransactionsPerBlock(DataCache snapshot) { - StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_MaxTransactionsPerBlock)); - if (item is null) return 512; - return (uint)(BigInteger)item; + return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_MaxTransactionsPerBlock)]; } [ContractMethod(0_01000000, CallFlags.ReadStates)] public uint GetMaxBlockSize(DataCache snapshot) { - StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_MaxBlockSize)); - if (item is null) return 1024 * 256; - return (uint)(BigInteger)item; + return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_MaxBlockSize)]; } [ContractMethod(0_01000000, CallFlags.ReadStates)] public long GetMaxBlockSystemFee(DataCache snapshot) { - StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_MaxBlockSystemFee)); - if (item is null) return 9000 * (long)GAS.Factor; // For the transfer method of NEP5, the maximum persisting time is about three seconds. - return (long)(BigInteger)item; + return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_MaxBlockSystemFee)]; } [ContractMethod(0_01000000, CallFlags.ReadStates)] public long GetFeePerByte(DataCache snapshot) { - StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_FeePerByte)); - if (item is null) return 1000; - return (long)(BigInteger)item; + return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_FeePerByte)]; } [ContractMethod(0_01000000, CallFlags.ReadStates)] public uint GetExecFeeFactor(DataCache snapshot) { - StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_ExecFeeFactor)); - if (item is null) return DefaultExecFeeFactor; - return (uint)(BigInteger)item; + return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_ExecFeeFactor)]; } [ContractMethod(0_01000000, CallFlags.ReadStates)] public uint GetStoragePrice(DataCache snapshot) { - StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_StoragePrice)); - if (item is null) return DefaultStoragePrice; - return (uint)(BigInteger)item; + return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_StoragePrice)]; } [ContractMethod(0_01000000, CallFlags.ReadStates)] @@ -86,8 +84,7 @@ private void SetMaxBlockSize(ApplicationEngine engine, uint value) { if (value > Message.PayloadMaxSize) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize), () => new StorageItem()); - storage.Set(value); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize)).Set(value); } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -95,8 +92,7 @@ private void SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value) { if (value > Block.MaxTransactionsPerBlock) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock), () => new StorageItem()); - storage.Set(value); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock)).Set(value); } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -104,8 +100,7 @@ private void SetMaxBlockSystemFee(ApplicationEngine engine, long value) { if (value <= 4007600) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSystemFee), () => new StorageItem()); - storage.Set(value); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSystemFee)).Set(value); } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -113,8 +108,7 @@ private void SetFeePerByte(ApplicationEngine engine, long value) { if (value < 0 || value > 1_00000000) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_FeePerByte), () => new StorageItem()); - storage.Set(value); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_FeePerByte)).Set(value); } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -122,8 +116,7 @@ private void SetExecFeeFactor(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxExecFeeFactor) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_ExecFeeFactor), () => new StorageItem()); - storage.Set(value); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_ExecFeeFactor)).Set(value); } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -131,8 +124,7 @@ private void SetStoragePrice(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxStoragePrice) throw new ArgumentOutOfRangeException(nameof(value)); if (!CheckCommittee(engine)) throw new InvalidOperationException(); - StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_StoragePrice), () => new StorageItem()); - storage.Set(value); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_StoragePrice)).Set(value); } [ContractMethod(0_03000000, CallFlags.WriteStates)] @@ -143,7 +135,7 @@ private bool BlockAccount(ApplicationEngine engine, UInt160 account) var key = CreateStorageKey(Prefix_BlockedAccount).Add(account); if (engine.Snapshot.Contains(key)) return false; - engine.Snapshot.Add(key, new StorageItem(new byte[] { 0x01 })); + engine.Snapshot.Add(key, new StorageItem(Array.Empty())); return true; } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 309707d005..ede06e3665 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -7,7 +7,6 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; -using Neo.UnitTests.IO.Caching; using System; using System.Linq; @@ -207,12 +206,11 @@ public void Check_SetFeePerByte() [TestMethod] public void Check_SetBaseExecFee() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - NativeContract.Policy.Initialize(ApplicationEngine.Create(TriggerType.Application, null, snapshot, block, 0)); // Without signature @@ -251,12 +249,11 @@ public void Check_SetBaseExecFee() [TestMethod] public void Check_SetStoragePrice() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - NativeContract.Policy.Initialize(ApplicationEngine.Create(TriggerType.Application, null, snapshot, block, 0)); // Without signature From 5c88a99b6f39145e3f6c7ee46dc5bddcdbb20a25 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 27 Jan 2021 17:29:40 +0800 Subject: [PATCH 55/65] Check script and abi when deploying contracts (#2263) --- src/neo/SmartContract/Helper.cs | 84 +++++++++++++++++++ src/neo/SmartContract/Manifest/ContractAbi.cs | 4 +- .../Manifest/ContractEventDescriptor.cs | 7 +- .../SmartContract/Manifest/ContractGroup.cs | 4 +- .../Manifest/ContractManifest.cs | 19 ++++- .../Manifest/ContractMethodDescriptor.cs | 14 ++-- .../Manifest/ContractParameterDefinition.cs | 7 +- .../Manifest/ContractPermission.cs | 6 +- .../Manifest/ContractPermissionDescriptor.cs | 22 ++++- .../Native/ContractManagement.cs | 8 +- .../Manifest/UT_ContractManifest.cs | 12 +-- .../SmartContract/UT_InteropService.NEO.cs | 7 +- tests/neo.UnitTests/TestUtils.cs | 13 ++- 13 files changed, 178 insertions(+), 29 deletions(-) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index bf8a6eae75..71dbb2baf3 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -9,6 +9,7 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; +using System.Linq; namespace Neo.SmartContract { @@ -29,6 +30,89 @@ public static long MultiSignatureContractCost(int m, int n) => ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.ECDsaVerifyPrice * n; + public static bool Check(Script script, ContractAbi abi = null) + { + Dictionary instructions = new Dictionary(); + for (int ip = 0; ip < script.Length;) + { + Instruction instruction = script.GetInstruction(ip); + instructions.Add(ip, instruction); + ip += instruction.Size; + } + foreach (var (ip, instruction) in instructions) + { + switch (instruction.OpCode) + { + case OpCode.JMP: + case OpCode.JMPIF: + case OpCode.JMPIFNOT: + case OpCode.JMPEQ: + case OpCode.JMPNE: + case OpCode.JMPGT: + case OpCode.JMPGE: + case OpCode.JMPLT: + case OpCode.JMPLE: + case OpCode.CALL: + case OpCode.ENDTRY: + if (!instructions.ContainsKey(checked(ip + instruction.TokenI8))) + return false; + break; + case OpCode.PUSHA: + case OpCode.JMP_L: + case OpCode.JMPIF_L: + case OpCode.JMPIFNOT_L: + case OpCode.JMPEQ_L: + case OpCode.JMPNE_L: + case OpCode.JMPGT_L: + case OpCode.JMPGE_L: + case OpCode.JMPLT_L: + case OpCode.JMPLE_L: + case OpCode.CALL_L: + case OpCode.ENDTRY_L: + if (!instructions.ContainsKey(checked(ip + instruction.TokenI32))) + return false; + break; + case OpCode.TRY: + if (!instructions.ContainsKey(checked(ip + instruction.TokenI8))) + return false; + if (!instructions.ContainsKey(checked(ip + instruction.TokenI8_1))) + return false; + break; + case OpCode.TRY_L: + if (!instructions.ContainsKey(checked(ip + instruction.TokenI32))) + return false; + if (!instructions.ContainsKey(checked(ip + instruction.TokenI32_1))) + return false; + break; + case OpCode.NEWARRAY_T: + case OpCode.ISTYPE: + case OpCode.CONVERT: + StackItemType type = (StackItemType)instruction.TokenU8; + if (!Enum.IsDefined(typeof(StackItemType), type)) + return false; + if (instruction.OpCode != OpCode.NEWARRAY_T && type == StackItemType.Any) + return false; + break; + } + } + if (abi is null) return true; + foreach (ContractMethodDescriptor method in abi.Methods) + { + if (!instructions.ContainsKey(method.Offset)) + return false; + } + try + { + abi.GetMethod(string.Empty, 0); // Trigger the construction of ContractAbi.methodDictionary to check the uniqueness of the method names. + _ = abi.Events.ToDictionary(p => p.Name); // Check the uniqueness of the event names. + } + catch (ArgumentException) + { + return false; + } + return true; + } + public static UInt160 GetContractHash(UInt160 sender, uint nefCheckSum, string name) { using var sb = new ScriptBuilder(); diff --git a/src/neo/SmartContract/Manifest/ContractAbi.cs b/src/neo/SmartContract/Manifest/ContractAbi.cs index b2d890b111..db4fdfe344 100644 --- a/src/neo/SmartContract/Manifest/ContractAbi.cs +++ b/src/neo/SmartContract/Manifest/ContractAbi.cs @@ -48,11 +48,13 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) /// Return ContractAbi public static ContractAbi FromJson(JObject json) { - return new ContractAbi + ContractAbi abi = new ContractAbi { Methods = ((JArray)json["methods"]).Select(u => ContractMethodDescriptor.FromJson(u)).ToArray(), Events = ((JArray)json["events"]).Select(u => ContractEventDescriptor.FromJson(u)).ToArray() }; + if (abi.Methods.Length == 0) throw new FormatException(); + return abi; } public ContractMethodDescriptor GetMethod(string name, int pcount) diff --git a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs index ea5787332c..7d8afd1121 100644 --- a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -1,7 +1,9 @@ using Neo.IO.Json; using Neo.VM; using Neo.VM.Types; +using System; using System.Linq; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Manifest { @@ -40,11 +42,14 @@ public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) /// Return ContractEventDescriptor public static ContractEventDescriptor FromJson(JObject json) { - return new ContractEventDescriptor + ContractEventDescriptor descriptor = new ContractEventDescriptor { Name = json["name"].AsString(), Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), }; + if (string.IsNullOrEmpty(descriptor.Name)) throw new FormatException(); + _ = descriptor.Parameters.ToDictionary(p => p.Name); + return descriptor; } public virtual JObject ToJson() diff --git a/src/neo/SmartContract/Manifest/ContractGroup.cs b/src/neo/SmartContract/Manifest/ContractGroup.cs index 9d1caa5250..b39c44c0dd 100644 --- a/src/neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/neo/SmartContract/Manifest/ContractGroup.cs @@ -43,11 +43,13 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) /// Return ContractManifestGroup public static ContractGroup FromJson(JObject json) { - return new ContractGroup + ContractGroup group = new ContractGroup { PubKey = ECPoint.Parse(json["pubkey"].AsString(), ECCurve.Secp256r1), Signature = Convert.FromBase64String(json["signature"].AsString()), }; + if (group.Signature.Length != 64) throw new FormatException(); + return group; } /// diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs index f356639a1d..71d93020f2 100644 --- a/src/neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/neo/SmartContract/Manifest/ContractManifest.cs @@ -93,7 +93,7 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) /// Return ContractManifest public static ContractManifest FromJson(JObject json) { - return new ContractManifest + ContractManifest manifest = new ContractManifest { Name = json["name"].AsString(), Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson(u)).ToArray(), @@ -103,6 +103,15 @@ public static ContractManifest FromJson(JObject json) Trusts = WildcardContainer.FromJson(json["trusts"], u => UInt160.Parse(u.AsString())), Extra = json["extra"] }; + if (string.IsNullOrEmpty(manifest.Name)) + throw new FormatException(); + _ = manifest.Groups.ToDictionary(p => p.PubKey); + if (manifest.SupportedStandards.Any(p => string.IsNullOrEmpty(p))) + throw new FormatException(); + _ = manifest.SupportedStandards.ToDictionary(p => p); + _ = manifest.Permissions.ToDictionary(p => p.Contract); + _ = manifest.Trusts.ToDictionary(p => p); + return manifest; } /// @@ -110,9 +119,13 @@ public static ContractManifest FromJson(JObject json) /// /// Json /// Return ContractManifest - public static ContractManifest Parse(ReadOnlySpan json) => FromJson(JObject.Parse(json)); + public static ContractManifest Parse(ReadOnlySpan json) + { + if (json.Length > MaxLength) throw new ArgumentException(null, nameof(json)); + return FromJson(JObject.Parse(json)); + } - public static ContractManifest Parse(string json) => FromJson(JObject.Parse(json)); + public static ContractManifest Parse(string json) => Parse(Utility.StrictUTF8.GetBytes(json)); /// public ContractParameterType ReturnType { get; set; } - private int _offset; - public int Offset - { - get => _offset; - set => _offset = value >= 0 ? value : throw new FormatException(); - } + public int Offset { get; set; } /// /// Determine if it's safe to call this method @@ -52,7 +47,7 @@ public override StackItem ToStackItem(ReferenceCounter referenceCounter) /// Return ContractMethodDescription public new static ContractMethodDescriptor FromJson(JObject json) { - return new ContractMethodDescriptor + ContractMethodDescriptor descriptor = new ContractMethodDescriptor { Name = json["name"].AsString(), Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), @@ -60,6 +55,11 @@ public override StackItem ToStackItem(ReferenceCounter referenceCounter) Offset = (int)json["offset"].AsNumber(), Safe = json["safe"].AsBoolean(), }; + if (string.IsNullOrEmpty(descriptor.Name)) throw new FormatException(); + _ = descriptor.Parameters.ToDictionary(p => p.Name); + if (!Enum.IsDefined(descriptor.ReturnType)) throw new FormatException(); + if (descriptor.Offset < 0) throw new FormatException(); + return descriptor; } public override JObject ToJson() diff --git a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs index 71f4f2391f..4bfed05262 100644 --- a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -37,11 +37,16 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) /// Return ContractParameterDefinition public static ContractParameterDefinition FromJson(JObject json) { - return new ContractParameterDefinition + ContractParameterDefinition parameter = new ContractParameterDefinition { Name = json["name"].AsString(), Type = (ContractParameterType)Enum.Parse(typeof(ContractParameterType), json["type"].AsString()), }; + if (string.IsNullOrEmpty(parameter.Name)) + throw new FormatException(); + if (!Enum.IsDefined(parameter.Type) || parameter.Type == ContractParameterType.Void) + throw new FormatException(); + return parameter; } public virtual JObject ToJson() diff --git a/src/neo/SmartContract/Manifest/ContractPermission.cs b/src/neo/SmartContract/Manifest/ContractPermission.cs index 37658df284..14c2608349 100644 --- a/src/neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/neo/SmartContract/Manifest/ContractPermission.cs @@ -63,11 +63,15 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) /// Return ContractPermission public static ContractPermission FromJson(JObject json) { - return new ContractPermission + ContractPermission permission = new ContractPermission { Contract = ContractPermissionDescriptor.FromJson(json["contract"]), Methods = WildcardContainer.FromJson(json["methods"], u => u.AsString()), }; + if (permission.Methods.Any(p => string.IsNullOrEmpty(p))) + throw new FormatException(); + _ = permission.Methods.ToDictionary(p => p); + return permission; } /// { public UInt160 Hash { get; } public ECPoint Group { get; } @@ -50,6 +50,26 @@ public static ContractPermissionDescriptor CreateWildcard() return new ContractPermissionDescriptor(null, null); } + public override bool Equals(object obj) + { + if (obj is not ContractPermissionDescriptor other) return false; + return Equals(other); + } + + public bool Equals(ContractPermissionDescriptor other) + { + if (other is null) return false; + if (this == other) return true; + if (IsWildcard == other.IsWildcard) return true; + if (IsHash) return Hash.Equals(other.Hash); + else return Group.Equals(other.Group); + } + + public override int GetHashCode() + { + return HashCode.Combine(Hash, Group); + } + public static ContractPermissionDescriptor FromJson(JObject json) { string str = json.AsString(); diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 416bfa594a..5f288791a3 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -133,7 +133,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma throw new InvalidOperationException(); if (nefFile.Length == 0) throw new ArgumentException($"Invalid NefFile Length: {nefFile.Length}"); - if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) + if (manifest.Length == 0) throw new ArgumentException($"Invalid Manifest Length: {manifest.Length}"); engine.AddGas(Math.Max( @@ -143,6 +143,8 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma NefFile nef = nefFile.AsSerializable(); ContractManifest parsedManifest = ContractManifest.Parse(manifest); + if (!Helper.Check(nef.Script, parsedManifest.Abi)) + throw new FormatException(); UInt160 hash = Helper.GetContractHash(tx.Sender, nef.CheckSum, parsedManifest.Name); StorageKey key = CreateStorageKey(Prefix_Contract).Add(hash); if (engine.Snapshot.Contains(key)) @@ -197,7 +199,7 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, S } if (manifest != null) { - if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) + if (manifest.Length == 0) throw new ArgumentException($"Invalid Manifest Length: {manifest.Length}"); ContractManifest manifest_new = ContractManifest.Parse(manifest); if (manifest_new.Name != contract.Manifest.Name) @@ -206,6 +208,8 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, S throw new InvalidOperationException($"Invalid Manifest Hash: {contract.Hash}"); contract.Manifest = manifest_new; } + if (!Helper.Check(contract.Nef.Script, contract.Manifest.Abi)) + throw new FormatException(); contract.UpdateCounter++; // Increase update counter if (nefFile != null) { diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index df448e2e8a..d9a0d4950c 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -12,7 +12,7 @@ public class UT_ContractManifest [TestMethod] public void ParseFromJson_Default() { - var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; + var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToJson().ToString(), json); @@ -23,7 +23,7 @@ public void ParseFromJson_Default() [TestMethod] public void ParseFromJson_Permissions() { - var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""extra"":null}"; + var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToJson().ToString(), json); @@ -42,7 +42,7 @@ public void ParseFromJson_Permissions() [TestMethod] public void ParseFromJson_SafeMethods() { - var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; + var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToJson().ToString(), json); @@ -53,7 +53,7 @@ public void ParseFromJson_SafeMethods() [TestMethod] public void ParseFromJson_Trust() { - var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""extra"":null}"; + var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToJson().ToString(), json); @@ -65,7 +65,7 @@ public void ParseFromJson_Trust() [TestMethod] public void ParseFromJson_Groups() { - var json = @"{""name"":""testManifest"",""groups"":[{""pubkey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""supportedstandards"":[],""abi"":{""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; + var json = @"{""name"":""testManifest"",""groups"":[{""pubkey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""supportedstandards"":[],""abi"":{""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToJson().ToString(), json); @@ -77,7 +77,7 @@ public void ParseFromJson_Groups() [TestMethod] public void ParseFromJson_Extra() { - var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":{""key"":""value""}}"; + var json = @"{""name"":""testManifest"",""groups"":[],""supportedstandards"":[],""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[{""name"":""testMethod"",""parameters"":[],""returntype"":""Void"",""offset"":0,""safe"":true}],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""extra"":{""key"":""value""}}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(json, json); Assert.AreEqual("value", manifest.Extra["key"].AsString(), false); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index d721c3936a..32c48095a2 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -11,6 +11,7 @@ using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; +using Neo.VM; using Neo.VM.Types; using Neo.Wallets; using System; @@ -133,7 +134,7 @@ public void TestContract_Create() var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); var nef = new NefFile() { - Script = new byte[byte.MaxValue], + Script = Enumerable.Repeat((byte)OpCode.RET, byte.MaxValue).ToArray(), Compiler = "", Tokens = System.Array.Empty() }; @@ -161,7 +162,7 @@ public void TestContract_Create() manifest = TestUtils.CreateDefaultManifest(); var ret = snapshot.DeployContract(UInt160.Zero, nefFile, manifest.ToJson().ToByteArray(false)); - ret.Hash.ToString().Should().Be("0x18e26e71e66fb79347581771c0910df5bea9d24b"); + ret.Hash.ToString().Should().Be("0x7b37d4bd3d87f53825c3554bd1a617318235a685"); Assert.ThrowsException(() => snapshot.DeployContract(UInt160.Zero, nefFile, manifest.ToJson().ToByteArray(false))); var state = TestUtils.GetContract(); @@ -176,7 +177,7 @@ public void TestContract_Update() var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); var nef = new NefFile() { - Script = new byte[] { 0x01 }, + Script = new[] { (byte)OpCode.RET }, Compiler = "", Tokens = System.Array.Empty() }; diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index 76646ef3f9..f68e058d01 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Neo.IO; using Neo.IO.Json; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Manifest; @@ -28,7 +27,17 @@ public static ContractManifest CreateDefaultManifest() Abi = new ContractAbi() { Events = new ContractEventDescriptor[0], - Methods = new ContractMethodDescriptor[0] + Methods = new[] + { + new ContractMethodDescriptor + { + Name = "testMethod", + Parameters = new ContractParameterDefinition[0], + ReturnType = ContractParameterType.Void, + Offset = 0, + Safe = true + } + } }, Permissions = new[] { ContractPermission.DefaultPermission }, Trusts = WildcardContainer.Create(), From 7938b93f8e624651df6c4e40f1e0be2e14e1291d Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 27 Jan 2021 21:28:53 +0800 Subject: [PATCH 56/65] Add tokens to NFT native contract (#2271) --- .../SmartContract/Native/NonfungibleToken.cs | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index 35f799a57f..c572a3889d 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -1,3 +1,5 @@ +#pragma warning disable IDE0051 + using Neo.IO; using Neo.Persistence; using Neo.SmartContract.Iterators; @@ -121,19 +123,18 @@ public BigInteger BalanceOf(DataCache snapshot, UInt160 owner) } [ContractMethod(0_01000000, CallFlags.ReadStates)] - public IIterator TokensOf(DataCache snapshot, UInt160 owner) + private IIterator Tokens(DataCache snapshot) { - if (owner is null) - { - var results = snapshot.Find(new[] { Prefix_Token }).GetEnumerator(); - return new StorageIterator(results, FindOptions.ValuesOnly | FindOptions.DeserializeValues | FindOptions.PickField1, null); - } - else - { - NFTAccountState account = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable(); - IReadOnlyList tokens = account?.Tokens ?? (IReadOnlyList)System.Array.Empty(); - return new ArrayWrapper(tokens.Select(p => (StackItem)p).ToArray()); - } + var results = snapshot.Find(new[] { Prefix_Token }).GetEnumerator(); + return new StorageIterator(results, FindOptions.ValuesOnly | FindOptions.DeserializeValues | FindOptions.PickField1, null); + } + + [ContractMethod(0_01000000, CallFlags.ReadStates)] + private IIterator TokensOf(DataCache snapshot, UInt160 owner) + { + NFTAccountState account = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable(); + IReadOnlyList tokens = account?.Tokens ?? (IReadOnlyList)System.Array.Empty(); + return new ArrayWrapper(tokens.Select(p => (StackItem)p).ToArray()); } [ContractMethod(0_09000000, CallFlags.WriteStates | CallFlags.AllowNotify)] From 234350dbaf724628ef22226c2db7b9d23cb3a9b9 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 28 Jan 2021 18:37:33 +0800 Subject: [PATCH 57/65] Remove currentSnapshot (#2273) --- src/neo/Ledger/Blockchain.cs | 71 ++++++------ src/neo/Ledger/MemoryPool.cs | 14 +-- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 58 +++++----- src/neo/Network/P2P/RemoteNode.cs | 3 +- src/neo/Network/P2P/TaskManager.cs | 28 ++--- src/neo/SmartContract/ApplicationEngine.cs | 11 +- .../ContractParametersContext.cs | 13 +-- src/neo/Wallets/AssetDescriptor.cs | 2 +- src/neo/Wallets/Wallet.cs | 101 +++++++++--------- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 12 --- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 2 - 11 files changed, 139 insertions(+), 176 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index e7bb4bcfba..8a3ac9c6d4 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -65,14 +65,15 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu private readonly ConcurrentDictionary block_cache = new ConcurrentDictionary(); private readonly Dictionary block_cache_unverified = new Dictionary(); internal readonly RelayCache RelayCache = new RelayCache(100); - private SnapshotCache currentSnapshot; private ImmutableHashSet extensibleWitnessWhiteList; public IStore Store { get; } + /// + /// A readonly view of the blockchain store. + /// Note: It doesn't need to be disposed because the inside it is null. + /// public DataCache View => new SnapshotCache(Store); public MemoryPool MemPool { get; } - public uint Height => NativeContract.Ledger.CurrentIndex(currentSnapshot); - public UInt256 CurrentBlockHash => NativeContract.Ledger.CurrentHash(currentSnapshot); private static Blockchain singleton; public static Blockchain Singleton @@ -110,14 +111,14 @@ public Blockchain(NeoSystem system, IStore store) { if (singleton != null) throw new InvalidOperationException(); - if (!NativeContract.Ledger.Initialized(View)) + DataCache snapshot = View; + if (!NativeContract.Ledger.Initialized(snapshot)) { Persist(GenesisBlock); } else { - UpdateCurrentSnapshot(); - MemPool.LoadPolicy(currentSnapshot); + UpdateExtensibleWitnessWhiteList(snapshot); } singleton = this; } @@ -136,14 +137,16 @@ public SnapshotCache GetSnapshot() private void OnImport(IEnumerable blocks, bool verify) { + uint currentHeight = NativeContract.Ledger.CurrentIndex(View); foreach (Block block in blocks) { - if (block.Index <= Height) continue; - if (block.Index != Height + 1) + if (block.Index <= currentHeight) continue; + if (block.Index != currentHeight + 1) throw new InvalidOperationException(); - if (verify && !block.Verify(currentSnapshot)) + if (verify && !block.Verify(View)) throw new InvalidOperationException(); Persist(block); + ++currentHeight; } Sender.Tell(new ImportCompleted()); } @@ -182,15 +185,17 @@ private void OnFillMemoryPool(IEnumerable transactions) // Invalidate all the transactions in the memory pool, to avoid any failures when adding new transactions. MemPool.InvalidateAllTransactions(); + DataCache snapshot = View; + // Add the transactions to the memory pool foreach (var tx in transactions) { - if (NativeContract.Ledger.ContainsTransaction(View, tx.Hash)) + if (NativeContract.Ledger.ContainsTransaction(snapshot, tx.Hash)) continue; // First remove the tx if it is unverified in the pool. MemPool.TryRemoveUnVerified(tx.Hash, out _); // Add to the memory pool - MemPool.TryAdd(tx, currentSnapshot); + MemPool.TryAdd(tx, snapshot); } // Transactions originally in the pool will automatically be reverified based on their priority. @@ -214,35 +219,37 @@ private void OnInventory(IInventory inventory, bool relay = true) private VerifyResult OnNewBlock(Block block) { - if (block.Index <= Height) + DataCache snapshot = View; + uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); + if (block.Index <= currentHeight) return VerifyResult.AlreadyExists; - if (block.Index - 1 > Height) + if (block.Index - 1 > currentHeight) { AddUnverifiedBlockToCache(block); return VerifyResult.UnableToVerify; } - if (block.Index == Height + 1) + if (block.Index == currentHeight + 1) { - if (!block.Verify(currentSnapshot)) + if (!block.Verify(snapshot)) return VerifyResult.Invalid; block_cache.TryAdd(block.Hash, block); block_cache_unverified.Remove(block.Index); Persist(block); - if (block_cache_unverified.TryGetValue(Height + 1, out var unverifiedBlocks)) + if (block_cache_unverified.TryGetValue(block.Index + 1, out var unverifiedBlocks)) { foreach (var unverifiedBlock in unverifiedBlocks.Blocks) Self.Tell(unverifiedBlock, ActorRefs.NoSender); - block_cache_unverified.Remove(Height + 1); + block_cache_unverified.Remove(block.Index + 1); } // We can store the new block in block_cache and tell the new height to other nodes after Persist(). - system.LocalNode.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(Singleton.Height))); + system.LocalNode.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(block.Index))); } return VerifyResult.Succeed; } private VerifyResult OnNewInventory(IInventory inventory) { - if (!inventory.Verify(currentSnapshot)) return VerifyResult.Invalid; + if (!inventory.Verify(View)) return VerifyResult.Invalid; RelayCache.Add(inventory); return VerifyResult.Succeed; } @@ -250,7 +257,7 @@ private VerifyResult OnNewInventory(IInventory inventory) private VerifyResult OnNewTransaction(Transaction transaction) { if (ContainsTransaction(transaction.Hash)) return VerifyResult.AlreadyExists; - return MemPool.TryAdd(transaction, currentSnapshot); + return MemPool.TryAdd(transaction, View); } private void OnPreverifyCompleted(PreverifyCompleted task) @@ -284,7 +291,7 @@ protected override void OnReceive(object message) OnPreverifyCompleted(task); break; case Idle _: - if (MemPool.ReVerifyTopUnverifiedTransactionsIfNeeded(MaxTxToReverifyPerIdle, currentSnapshot)) + if (MemPool.ReVerifyTopUnverifiedTransactionsIfNeeded(MaxTxToReverifyPerIdle, View)) Self.Tell(Idle.Instance, ActorRefs.NoSender); break; } @@ -361,19 +368,13 @@ private void Persist(Block block) } } if (commitExceptions != null) throw new AggregateException(commitExceptions); + UpdateExtensibleWitnessWhiteList(snapshot); + MemPool.UpdatePoolForBlockPersisted(block, snapshot); } - UpdateCurrentSnapshot(); block_cache.TryRemove(block.PrevHash, out _); - MemPool.UpdatePoolForBlockPersisted(block, currentSnapshot); Context.System.EventStream.Publish(new PersistCompleted { Block = block }); } - protected override void PostStop() - { - base.PostStop(); - currentSnapshot?.Dispose(); - } - public static Props Props(NeoSystem system, IStore store) { return Akka.Actor.Props.Create(() => new Blockchain(system, store)).WithMailbox("blockchain-mailbox"); @@ -390,21 +391,21 @@ private void SendRelayResult(IInventory inventory, VerifyResult result) Context.System.EventStream.Publish(rr); } - private void UpdateCurrentSnapshot() + private void UpdateExtensibleWitnessWhiteList(DataCache snapshot) { - Interlocked.Exchange(ref currentSnapshot, GetSnapshot())?.Dispose(); + uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); var builder = ImmutableHashSet.CreateBuilder(); - builder.Add(NativeContract.NEO.GetCommitteeAddress(currentSnapshot)); - var validators = NativeContract.NEO.GetNextBlockValidators(currentSnapshot); + builder.Add(NativeContract.NEO.GetCommitteeAddress(snapshot)); + var validators = NativeContract.NEO.GetNextBlockValidators(snapshot); builder.Add(Contract.GetBFTAddress(validators)); builder.UnionWith(validators.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); - var oracles = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.Oracle, Height); + var oracles = NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.Oracle, currentHeight); if (oracles.Length > 0) { builder.Add(Contract.GetBFTAddress(oracles)); builder.UnionWith(oracles.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); } - var stateValidators = NativeContract.RoleManagement.GetDesignatedByRole(currentSnapshot, Role.StateValidator, Height); + var stateValidators = NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.StateValidator, currentHeight); if (stateValidators.Length > 0) { builder.Add(Contract.GetBFTAddress(stateValidators)); diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index bc15638fef..be505878b6 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -59,8 +59,6 @@ public class MemoryPool : IReadOnlyCollection internal int SortedTxCount => _sortedTransactions.Count; internal int UnverifiedSortedTxCount => _unverifiedSortedTransactions.Count; - private int _maxTxPerBlock; - /// /// Total maximum capacity of transactions the pool can hold. /// @@ -103,11 +101,6 @@ public MemoryPool(NeoSystem system, int capacity) Capacity = capacity; } - internal void LoadPolicy(DataCache snapshot) - { - _maxTxPerBlock = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); - } - /// /// Determine whether the pool is holding this transaction and has at some point verified it. /// Note: The pool may not have verified it since the last block was persisted. To get only the @@ -350,8 +343,6 @@ internal void InvalidateVerifiedTransactions() // Note: this must only be called from a single thread (the Blockchain actor) internal void UpdatePoolForBlockPersisted(Block block, DataCache snapshot) { - LoadPolicy(snapshot); - _txRwLock.EnterWriteLock(); try { @@ -370,8 +361,8 @@ internal void UpdatePoolForBlockPersisted(Block block, DataCache snapshot) _txRwLock.ExitWriteLock(); } - ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, - _maxTxPerBlock, MaxMillisecondsToReverifyTx, snapshot); + uint _maxTxPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); + ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, (int)_maxTxPerBlock, MaxMillisecondsToReverifyTx, snapshot); } internal void InvalidateAllTransactions() @@ -468,6 +459,7 @@ internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, DataCac { if (_unverifiedSortedTransactions.Count > 0) { + uint _maxTxPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); int verifyCount = _sortedTransactions.Count > _maxTxPerBlock ? 1 : maxToVerify; ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, verifyCount, MaxMillisecondsToReverifyTxPerIdle, snapshot); diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 0cb6b1afcb..fb9fd5cc5b 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -170,23 +170,22 @@ private void OnGetAddrMessageReceived() /// A GetBlocksPayload including start block Hash and number of blocks requested. private void OnGetBlocksMessageReceived(GetBlocksPayload payload) { + // The default value of payload.Count is -1 + int count = payload.Count < 0 || payload.Count > InvPayload.MaxHashesCount ? InvPayload.MaxHashesCount : payload.Count; + DataCache snapshot = Blockchain.Singleton.View; + UInt256 hash = payload.HashStart; + TrimmedBlock state = NativeContract.Ledger.GetTrimmedBlock(snapshot, hash); + if (state == null) return; + uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); List hashes = new List(); - using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) + for (uint i = 1; i <= count; i++) { - UInt256 hash = payload.HashStart; - // The default value of payload.Count is -1 - int count = payload.Count < 0 || payload.Count > InvPayload.MaxHashesCount ? InvPayload.MaxHashesCount : payload.Count; - TrimmedBlock state = NativeContract.Ledger.GetTrimmedBlock(snapshot, hash); - if (state == null) return; - for (uint i = 1; i <= count; i++) - { - uint index = state.Index + i; - if (index > Blockchain.Singleton.Height) - break; - hash = NativeContract.Ledger.GetBlockHash(snapshot, index); - if (hash == null) break; - hashes.Add(hash); - } + uint index = state.Index + i; + if (index > currentHeight) + break; + hash = NativeContract.Ledger.GetBlockHash(snapshot, index); + if (hash == null) break; + hashes.Add(hash); } if (hashes.Count == 0) return; EnqueueMessage(Message.Create(MessageCommand.Inv, InvPayload.Create(InventoryType.Block, hashes.ToArray()))); @@ -273,18 +272,15 @@ private void OnGetDataMessageReceived(InvPayload payload) /// A GetBlocksPayload including start block index and number of blocks' headers requested. private void OnGetHeadersMessageReceived(GetBlockByIndexPayload payload) { - uint index = payload.IndexStart; - if (index > Blockchain.Singleton.Height) return; + DataCache snapshot = Blockchain.Singleton.View; + if (payload.IndexStart > NativeContract.Ledger.CurrentIndex(snapshot)) return; List
headers = new List
(); - using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) + uint count = payload.Count == -1 ? HeadersPayload.MaxHeadersCount : (uint)payload.Count; + for (uint i = 0; i < count; i++) { - uint count = payload.Count == -1 ? HeadersPayload.MaxHeadersCount : (uint)payload.Count; - for (uint i = 0; i < count; i++) - { - var header = NativeContract.Ledger.GetHeader(snapshot, index + i); - if (header == null) break; - headers.Add(header); - } + var header = NativeContract.Ledger.GetHeader(snapshot, payload.IndexStart + i); + if (header == null) break; + headers.Add(header); } if (headers.Count == 0) return; EnqueueMessage(Message.Create(MessageCommand.Headers, HeadersPayload.Create(headers.ToArray()))); @@ -295,7 +291,7 @@ private void OnInventoryReceived(IInventory inventory) pendingKnownHashes.Remove(inventory.Hash); if (inventory is Block block) { - if (block.Index > Blockchain.Singleton.Height + InvPayload.MaxHashesCount) return; + if (block.Index > NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View) + InvPayload.MaxHashesCount) return; UpdateLastBlockIndex(block.Index, false); } knownHashes.Add(inventory.Hash); @@ -310,12 +306,16 @@ private void OnInvMessageReceived(InvPayload payload) switch (payload.Type) { case InventoryType.Block: - using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) + { + DataCache snapshot = Blockchain.Singleton.View; hashes = hashes.Where(p => !NativeContract.Ledger.ContainsBlock(snapshot, p)).ToArray(); + } break; case InventoryType.TX: - using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) + { + DataCache snapshot = Blockchain.Singleton.View; hashes = hashes.Where(p => !NativeContract.Ledger.ContainsTransaction(snapshot, p)).ToArray(); + } break; } if (hashes.Length == 0) return; @@ -333,7 +333,7 @@ private void OnMemPoolMessageReceived() private void OnPingMessageReceived(PingPayload payload) { UpdateLastBlockIndex(payload.LastBlockIndex, true); - EnqueueMessage(Message.Create(MessageCommand.Pong, PingPayload.Create(Blockchain.Singleton.Height, payload.Nonce))); + EnqueueMessage(Message.Create(MessageCommand.Pong, PingPayload.Create(NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View), payload.Nonce))); } private void OnPongMessageReceived(PingPayload payload) diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index 5fdc088b92..a417e4115e 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -7,6 +7,7 @@ using Neo.Ledger; using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; +using Neo.SmartContract.Native; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -173,7 +174,7 @@ private void OnStartProtocol() { var capabilities = new List { - new FullNodeCapability(Blockchain.Singleton.Height) + new FullNodeCapability(NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View)) }; if (LocalNode.Singleton.ListenerTcpPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.TcpServer, (ushort)LocalNode.Singleton.ListenerTcpPort)); diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 1e11baf4e0..52aa7013a5 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -46,14 +46,14 @@ public TaskManager(NeoSystem system) { this.system = system; this.knownHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); - this.lastTaskIndex = Blockchain.Singleton.Height; + this.lastTaskIndex = NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View); Context.System.EventStream.Subscribe(Self, typeof(Blockchain.PersistCompleted)); Context.System.EventStream.Subscribe(Self, typeof(Blockchain.RelayResult)); } private bool AssignSyncTask(uint index, TaskSession filterSession = null) { - if (index <= Blockchain.Singleton.Height || sessions.Values.Any(p => p != filterSession && p.IndexTasks.ContainsKey(index))) + if (index <= NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View) || sessions.Values.Any(p => p != filterSession && p.IndexTasks.ContainsKey(index))) return true; Random rand = new Random(); KeyValuePair remoteNode = sessions.Where(p => p.Value != filterSession && p.Value.LastBlockIndex >= index) @@ -96,7 +96,7 @@ private void OnNewTasks(InvPayload payload) if (!sessions.TryGetValue(Sender, out TaskSession session)) return; // Do not accept payload of type InventoryType.TX if not synced on best known HeaderHeight - if (payload.Type == InventoryType.TX && Blockchain.Singleton.Height < sessions.Values.Max(p => p.LastBlockIndex)) + if (payload.Type == InventoryType.TX && NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View) < sessions.Values.Max(p => p.LastBlockIndex)) return; HashSet hashes = new HashSet(payload.Hashes); // Remove all previously processed knownHashes from the list that is being requested @@ -276,14 +276,16 @@ public static Props Props(NeoSystem system) private void RequestTasks(bool sendPing) { - if (sessions.Count() == 0) return; + if (sessions.Count == 0) return; if (sendPing) SendPingMessage(); - while (failedSyncTasks.Count() > 0) + uint currentHeight = NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View); + + while (failedSyncTasks.Count > 0) { var failedTask = failedSyncTasks.First(); - if (failedTask <= Blockchain.Singleton.Height) + if (failedTask <= currentHeight) { failedSyncTasks.Remove(failedTask); continue; @@ -295,19 +297,17 @@ private void RequestTasks(bool sendPing) var highestBlockIndex = sessions.Values.Max(p => p.LastBlockIndex); for (; taskCounts < MaxSyncTasksCount; taskCounts++) { - if (lastTaskIndex >= highestBlockIndex || lastTaskIndex >= Blockchain.Singleton.Height + InvPayload.MaxHashesCount) break; + if (lastTaskIndex >= highestBlockIndex || lastTaskIndex >= currentHeight + InvPayload.MaxHashesCount) break; if (!AssignSyncTask(++lastTaskIndex)) break; } } private void SendPingMessage() { - TrimmedBlock block; - using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) - { - block = NativeContract.Ledger.GetTrimmedBlock(snapshot, NativeContract.Ledger.CurrentHash(snapshot)); - } - + DataCache snapshot = Blockchain.Singleton.View; + uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); + UInt256 currentHash = NativeContract.Ledger.CurrentHash(snapshot); + TrimmedBlock block = NativeContract.Ledger.GetTrimmedBlock(snapshot, currentHash); foreach (KeyValuePair item in sessions) { var node = item.Key; @@ -321,7 +321,7 @@ private void SendPingMessage() { node.Tell(Message.Create(MessageCommand.Mempool)); } - node.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(Blockchain.Singleton.Height))); + node.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(currentHeight))); session.ExpireTime = TimeProvider.Current.UtcNow.AddMilliseconds(PingCoolingOffPeriod); } } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index c5857228da..39c7f048cb 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -357,14 +357,9 @@ internal static void ResetApplicationEngineProvider() public static ApplicationEngine Run(byte[] script, DataCache snapshot = null, IVerifiable container = null, Block persistingBlock = null, int offset = 0, long gas = TestModeGas) { - SnapshotCache disposable = null; - if (snapshot is null) - { - disposable = Blockchain.Singleton.GetSnapshot(); - snapshot = disposable; - } - ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock ?? CreateDummyBlock(snapshot), gas); - if (disposable != null) engine.Disposables.Add(disposable); + snapshot ??= Blockchain.Singleton.View; + persistingBlock ??= CreateDummyBlock(snapshot); + ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock, gas); engine.LoadScript(script, initialPosition: offset); engine.Execute(); return engine; diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index 40d44b424e..c68f4974b9 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -2,7 +2,6 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.VM; using System; using System.Collections.Generic; @@ -84,19 +83,13 @@ public IReadOnlyList ScriptHashes { get { - if (_ScriptHashes == null) + if (_ScriptHashes is null) { // snapshot is not necessary for Transaction if (Verifiable is Transaction) - { _ScriptHashes = Verifiable.GetScriptHashesForVerifying(null); - return _ScriptHashes; - } - - using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) - { - _ScriptHashes = Verifiable.GetScriptHashesForVerifying(snapshot); - } + else + _ScriptHashes = Verifiable.GetScriptHashesForVerifying(Blockchain.Singleton.View); } return _ScriptHashes; } diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index 7cbcbdc1a4..15430e9a2b 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -16,7 +16,7 @@ public class AssetDescriptor public AssetDescriptor(UInt160 asset_id) { - using SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot(); + DataCache snapshot = Blockchain.Singleton.View; var contract = NativeContract.ContractManagement.GetContract(snapshot, asset_id); if (contract is null) throw new ArgumentException(); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index a0dc5c4945..b39bc6317a 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -252,64 +252,62 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null { accounts = new[] { from }; } - using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) + DataCache snapshot = Blockchain.Singleton.View; + Dictionary cosignerList = cosigners?.ToDictionary(p => p.Account) ?? new Dictionary(); + byte[] script; + List<(UInt160 Account, BigInteger Value)> balances_gas = null; + using (ScriptBuilder sb = new ScriptBuilder()) { - Dictionary cosignerList = cosigners?.ToDictionary(p => p.Account) ?? new Dictionary(); - byte[] script; - List<(UInt160 Account, BigInteger Value)> balances_gas = null; - using (ScriptBuilder sb = new ScriptBuilder()) + foreach (var (assetId, group, sum) in outputs.GroupBy(p => p.AssetId, (k, g) => (k, g, g.Select(p => p.Value.Value).Sum()))) { - foreach (var (assetId, group, sum) in outputs.GroupBy(p => p.AssetId, (k, g) => (k, g, g.Select(p => p.Value.Value).Sum()))) - { - var balances = new List<(UInt160 Account, BigInteger Value)>(); - foreach (UInt160 account in accounts) - using (ScriptBuilder sb2 = new ScriptBuilder()) + var balances = new List<(UInt160 Account, BigInteger Value)>(); + foreach (UInt160 account in accounts) + using (ScriptBuilder sb2 = new ScriptBuilder()) + { + sb2.EmitDynamicCall(assetId, "balanceOf", account); + using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot)) { - sb2.EmitDynamicCall(assetId, "balanceOf", account); - using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot)) - { - if (engine.State.HasFlag(VMState.FAULT)) - throw new InvalidOperationException($"Execution for {assetId.ToString()}.balanceOf('{account.ToString()}' fault"); - BigInteger value = engine.ResultStack.Pop().GetInteger(); - if (value.Sign > 0) balances.Add((account, value)); - } + if (engine.State.HasFlag(VMState.FAULT)) + throw new InvalidOperationException($"Execution for {assetId}.balanceOf('{account}' fault"); + BigInteger value = engine.ResultStack.Pop().GetInteger(); + if (value.Sign > 0) balances.Add((account, value)); } - BigInteger sum_balance = balances.Select(p => p.Value).Sum(); - if (sum_balance < sum) - throw new InvalidOperationException($"It does not have enough balance, expected: {sum.ToString()} found: {sum_balance.ToString()}"); - foreach (TransferOutput output in group) + } + BigInteger sum_balance = balances.Select(p => p.Value).Sum(); + if (sum_balance < sum) + throw new InvalidOperationException($"It does not have enough balance, expected: {sum} found: {sum_balance}"); + foreach (TransferOutput output in group) + { + balances = balances.OrderBy(p => p.Value).ToList(); + var balances_used = FindPayingAccounts(balances, output.Value.Value); + foreach (var (account, value) in balances_used) { - balances = balances.OrderBy(p => p.Value).ToList(); - var balances_used = FindPayingAccounts(balances, output.Value.Value); - foreach (var (account, value) in balances_used) + if (cosignerList.TryGetValue(account, out Signer signer)) { - if (cosignerList.TryGetValue(account, out Signer signer)) - { - if (signer.Scopes != WitnessScope.Global) - signer.Scopes |= WitnessScope.CalledByEntry; - } - else + if (signer.Scopes != WitnessScope.Global) + signer.Scopes |= WitnessScope.CalledByEntry; + } + else + { + cosignerList.Add(account, new Signer { - cosignerList.Add(account, new Signer - { - Account = account, - Scopes = WitnessScope.CalledByEntry - }); - } - sb.EmitDynamicCall(output.AssetId, "transfer", account, output.ScriptHash, value, output.Data); - sb.Emit(OpCode.ASSERT); + Account = account, + Scopes = WitnessScope.CalledByEntry + }); } + sb.EmitDynamicCall(output.AssetId, "transfer", account, output.ScriptHash, value, output.Data); + sb.Emit(OpCode.ASSERT); } - if (assetId.Equals(NativeContract.GAS.Hash)) - balances_gas = balances; } - script = sb.ToArray(); + if (assetId.Equals(NativeContract.GAS.Hash)) + balances_gas = balances; } - if (balances_gas is null) - balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); - - return MakeTransaction(snapshot, script, cosignerList.Values.ToArray(), Array.Empty(), balances_gas); + script = sb.ToArray(); } + if (balances_gas is null) + balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); + + return MakeTransaction(snapshot, script, cosignerList.Values.ToArray(), Array.Empty(), balances_gas); } public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Signer[] cosigners = null, TransactionAttribute[] attributes = null) @@ -323,11 +321,9 @@ public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Signer[ { accounts = new[] { sender }; } - using (SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot()) - { - var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); - return MakeTransaction(snapshot, script, cosigners ?? Array.Empty(), attributes ?? Array.Empty(), balances_gas); - } + DataCache snapshot = Blockchain.Singleton.View; + var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); + return MakeTransaction(snapshot, script, cosigners ?? Array.Empty(), attributes ?? Array.Empty(), balances_gas); } private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] cosigners, TransactionAttribute[] attributes, List<(UInt160 Account, BigInteger Value)> balances_gas) @@ -475,8 +471,7 @@ public bool Sign(ContractParametersContext context) // Try Smart contract verification - using var snapshot = Blockchain.Singleton.GetSnapshot(); - var contract = NativeContract.ContractManagement.GetContract(snapshot, scriptHash); + var contract = NativeContract.ContractManagement.GetContract(Blockchain.Singleton.View, scriptHash); if (contract != null) { diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 40ba38c868..9df5c96f6f 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -1,5 +1,4 @@ using Akka.TestKit.Xunit2; -using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Ledger; @@ -12,7 +11,6 @@ using System; using System.Linq; using System.Numerics; -using System.Reflection; namespace Neo.UnitTests.Ledger { @@ -62,12 +60,6 @@ public void Initialize() Blockchain.Singleton.MemPool.TryAdd(txSample, Blockchain.Singleton.GetSnapshot()); } - [TestMethod] - public void TestGetCurrentBlockHash() - { - Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0x00c6803707b564153d444bfcdf3a13325fc96dda55cc8a740bbd543a1d752fda")); - } - [TestMethod] public void TestValidTransaction() { @@ -85,10 +77,6 @@ public void TestValidTransaction() entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; snapshot.Commit(); - typeof(Blockchain) - .GetMethod("UpdateCurrentSnapshot", BindingFlags.Instance | BindingFlags.NonPublic) - .Invoke(Blockchain.Singleton, null); - // Make transaction var tx = CreateValidTx(walletA, acc.ScriptHash, 0); diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 8e8f31841c..a610c70ccf 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -52,7 +52,6 @@ public void TestSetup() // Create a MemoryPool with capacity of 100 _unit = new MemoryPool(TestBlockchain.TheNeoSystem, 100); - _unit.LoadPolicy(Blockchain.Singleton.GetSnapshot()); // Verify capacity equals the amount specified _unit.Capacity.Should().Be(100); @@ -429,7 +428,6 @@ public void TestGetVerifiedTransactions() public void TestReVerifyTopUnverifiedTransactionsIfNeeded() { _unit = new MemoryPool(TestBlockchain.TheNeoSystem, 600); - _unit.LoadPolicy(Blockchain.Singleton.GetSnapshot()); AddTransaction(CreateTransaction(100000001)); AddTransaction(CreateTransaction(100000001)); AddTransaction(CreateTransaction(100000001)); From 97707480b656ad1e2787ffaf1a373397c0643e84 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 29 Jan 2021 01:23:13 +0800 Subject: [PATCH 58/65] Check all transactions and witnesses (#2266) --- src/neo/Network/P2P/Payloads/Transaction.cs | 9 +- src/neo/SmartContract/Helper.cs | 109 ++++-------------- .../Native/ContractManagement.cs | 16 ++- src/neo/neo.csproj | 2 +- .../Ledger/UT_TransactionState.cs | 3 +- .../Network/P2P/Payloads/UT_Block.cs | 10 +- .../P2P/Payloads/UT_ExtensiblePayload.cs | 3 +- tests/neo.UnitTests/Network/P2P/UT_Message.cs | 10 +- .../UT_ContractParameterContext.cs | 6 +- .../SmartContract/UT_SmartContractHelper.cs | 2 +- tests/neo.UnitTests/TestUtils.cs | 2 +- 11 files changed, 62 insertions(+), 110 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 2007c8c202..5d8ffa4b3a 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -320,8 +320,15 @@ public virtual VerifyResult VerifyStateDependent(DataCache snapshot, Transaction public virtual VerifyResult VerifyStateIndependent() { - if (Size > MaxTransactionSize) + if (Size > MaxTransactionSize) return VerifyResult.Invalid; + try + { + _ = new Script(Script, true); + } + catch (BadScriptException) + { return VerifyResult.Invalid; + } UInt160[] hashes = GetScriptHashesForVerifying(null); if (hashes.Length != witnesses.Length) return VerifyResult.Invalid; for (int i = 0; i < hashes.Length; i++) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 71dbb2baf3..7d80688934 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -9,7 +9,6 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; -using System.Linq; namespace Neo.SmartContract { @@ -30,89 +29,6 @@ public static long MultiSignatureContractCost(int m, int n) => ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.ECDsaVerifyPrice * n; - public static bool Check(Script script, ContractAbi abi = null) - { - Dictionary instructions = new Dictionary(); - for (int ip = 0; ip < script.Length;) - { - Instruction instruction = script.GetInstruction(ip); - instructions.Add(ip, instruction); - ip += instruction.Size; - } - foreach (var (ip, instruction) in instructions) - { - switch (instruction.OpCode) - { - case OpCode.JMP: - case OpCode.JMPIF: - case OpCode.JMPIFNOT: - case OpCode.JMPEQ: - case OpCode.JMPNE: - case OpCode.JMPGT: - case OpCode.JMPGE: - case OpCode.JMPLT: - case OpCode.JMPLE: - case OpCode.CALL: - case OpCode.ENDTRY: - if (!instructions.ContainsKey(checked(ip + instruction.TokenI8))) - return false; - break; - case OpCode.PUSHA: - case OpCode.JMP_L: - case OpCode.JMPIF_L: - case OpCode.JMPIFNOT_L: - case OpCode.JMPEQ_L: - case OpCode.JMPNE_L: - case OpCode.JMPGT_L: - case OpCode.JMPGE_L: - case OpCode.JMPLT_L: - case OpCode.JMPLE_L: - case OpCode.CALL_L: - case OpCode.ENDTRY_L: - if (!instructions.ContainsKey(checked(ip + instruction.TokenI32))) - return false; - break; - case OpCode.TRY: - if (!instructions.ContainsKey(checked(ip + instruction.TokenI8))) - return false; - if (!instructions.ContainsKey(checked(ip + instruction.TokenI8_1))) - return false; - break; - case OpCode.TRY_L: - if (!instructions.ContainsKey(checked(ip + instruction.TokenI32))) - return false; - if (!instructions.ContainsKey(checked(ip + instruction.TokenI32_1))) - return false; - break; - case OpCode.NEWARRAY_T: - case OpCode.ISTYPE: - case OpCode.CONVERT: - StackItemType type = (StackItemType)instruction.TokenU8; - if (!Enum.IsDefined(typeof(StackItemType), type)) - return false; - if (instruction.OpCode != OpCode.NEWARRAY_T && type == StackItemType.Any) - return false; - break; - } - } - if (abi is null) return true; - foreach (ContractMethodDescriptor method in abi.Methods) - { - if (!instructions.ContainsKey(method.Offset)) - return false; - } - try - { - abi.GetMethod(string.Empty, 0); // Trigger the construction of ContractAbi.methodDictionary to check the uniqueness of the method names. - _ = abi.Events.ToDictionary(p => p.Name); // Check the uniqueness of the event names. - } - catch (ArgumentException) - { - return false; - } - return true; - } - public static UInt160 GetContractHash(UInt160 sender, uint nefCheckSum, string name) { using var sb = new ScriptBuilder(); @@ -272,12 +188,20 @@ public static bool VerifyWitnesses(this IVerifiable verifiable, DataCache snapsh internal static bool VerifyWitness(this IVerifiable verifiable, DataCache snapshot, UInt160 hash, Witness witness, long gas, out long fee) { fee = 0; + Script invocationScript; + try + { + invocationScript = new Script(witness.InvocationScript, true); + } + catch (BadScriptException) + { + return false; + } using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, verifiable, snapshot?.CreateSnapshot(), null, gas)) { CallFlags callFlags = !witness.VerificationScript.IsStandardContract() ? CallFlags.ReadStates : CallFlags.None; - byte[] verification = witness.VerificationScript; - if (verification.Length == 0) + if (witness.VerificationScript.Length == 0) { ContractState cs = NativeContract.ContractManagement.GetContract(snapshot, hash); if (cs is null) return false; @@ -289,14 +213,23 @@ internal static bool VerifyWitness(this IVerifiable verifiable, DataCache snapsh { if (NativeContract.IsNative(hash)) return false; if (hash != witness.ScriptHash) return false; - engine.LoadScript(verification, initialPosition: 0, configureState: p => + Script verificationScript; + try + { + verificationScript = new Script(witness.VerificationScript, true); + } + catch (BadScriptException) + { + return false; + } + engine.LoadScript(verificationScript, initialPosition: 0, configureState: p => { p.CallFlags = callFlags; p.ScriptHash = hash; }); } - engine.LoadScript(witness.InvocationScript, configureState: p => p.CallFlags = CallFlags.None); + engine.LoadScript(invocationScript, configureState: p => p.CallFlags = CallFlags.None); if (NativeContract.IsNative(hash)) { diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 5f288791a3..9d61edef62 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -4,6 +4,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; +using Neo.VM; using Neo.VM.Types; using System; using System.Collections.Generic; @@ -63,6 +64,15 @@ internal ContractManagement() Manifest.Abi.Events = events.ToArray(); } + private static void Check(byte[] script, ContractAbi abi) + { + Script s = new Script(script, true); + foreach (ContractMethodDescriptor method in abi.Methods) + s.GetInstruction(method.Offset); + abi.GetMethod(string.Empty, 0); // Trigger the construction of ContractAbi.methodDictionary to check the uniqueness of the method names. + _ = abi.Events.ToDictionary(p => p.Name); // Check the uniqueness of the event names. + } + private int GetNextAvailableId(DataCache snapshot) { StorageItem item = snapshot.GetAndChange(CreateStorageKey(Prefix_NextAvailableId)); @@ -143,8 +153,7 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma NefFile nef = nefFile.AsSerializable(); ContractManifest parsedManifest = ContractManifest.Parse(manifest); - if (!Helper.Check(nef.Script, parsedManifest.Abi)) - throw new FormatException(); + Check(nef.Script, parsedManifest.Abi); UInt160 hash = Helper.GetContractHash(tx.Sender, nef.CheckSum, parsedManifest.Name); StorageKey key = CreateStorageKey(Prefix_Contract).Add(hash); if (engine.Snapshot.Contains(key)) @@ -208,8 +217,7 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, S throw new InvalidOperationException($"Invalid Manifest Hash: {contract.Hash}"); contract.Manifest = manifest_new; } - if (!Helper.Check(contract.Nef.Script, contract.Manifest.Abi)) - throw new FormatException(); + Check(contract.Nef.Script, contract.Manifest.Abi); contract.UpdateCounter++; // Increase update counter if (nefFile != null) { diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index f079bc9509..224a8fde2b 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -29,7 +29,7 @@ - + diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs index 6d06e582a1..e57d3af1c4 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs +++ b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs @@ -3,6 +3,7 @@ using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.VM; using System; using System.IO; @@ -22,7 +23,7 @@ public void Initialize() Transaction = new Transaction() { Attributes = Array.Empty(), - Script = new byte[] { 0x01 }, + Script = new byte[] { (byte)OpCode.PUSH1 }, Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, Witnesses = new Witness[] { new Witness() { InvocationScript=Array.Empty(), diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index 533f277f9a..a1ea5635c1 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -84,7 +84,7 @@ public void Serialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 1); - var hex = "000000000000000000000000000000000000000000000000000000000000000000000000add6632f6f3d29cdf94555bb191fb5296683e5446f9937c56bb94c8608023044e913ff854c00000000000000000000000000000000000000000000000000000001000111020000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000100010000"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007ee5991fa69cf4d7902430f5fad89ba13b253b5680cb13167f80bfc3593947e7e913ff854c00000000000000000000000000000000000000000000000000000001000111020000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; uut.ToArray().ToHexString().Should().Be(hex); } @@ -94,7 +94,7 @@ public void Deserialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(new Block(), val256, out var merkRoot, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 1); - var hex = "000000000000000000000000000000000000000000000000000000000000000000000000add6632f6f3d29cdf94555bb191fb5296683e5446f9937c56bb94c8608023044e913ff854c00000000000000000000000000000000000000000000000000000001000111020000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000100010000"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007ee5991fa69cf4d7902430f5fad89ba13b253b5680cb13167f80bfc3593947e7e913ff854c00000000000000000000000000000000000000000000000000000001000111020000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) using (BinaryReader reader = new BinaryReader(ms)) @@ -178,11 +178,11 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0x9a164d5b9a1ab8745c97dbaaaef8eb30b0d80a00205acdc82daf502bee69bc20"); + jObj["hash"].AsString().Should().Be("0xaf156a193ba2d7f05a37dd4e6fdfd1167cb6db3df5474df43a71a645a449fbc8"); jObj["size"].AsNumber().Should().Be(167); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - jObj["merkleroot"].AsString().Should().Be("0x44300208864cb96bc537996f44e5836629b51f19bb5545f9cd293d6f2f63d6ad"); + jObj["merkleroot"].AsString().Should().Be("0xe7473959c3bf807f1613cb80563b253ba19bd8faf5302490d7f49ca61f99e57e"); jObj["time"].AsNumber().Should().Be(328665601001); jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); @@ -193,7 +193,7 @@ public void ToJson() jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; - txObj[0]["hash"].AsString().Should().Be("0x995ce8ff19c30f6b0d6b03e5ed8bd30b08027c92177923782d3a64f573421931"); + txObj[0]["hash"].AsString().Should().Be("0x0ee830a3b3e93679e23a9ee7e2a55287d79b6d67d056b03aa7ca917bd3d2923c"); txObj[0]["size"].AsNumber().Should().Be(53); txObj[0]["version"].AsNumber().Should().Be(0); ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs index 0fd01642b1..77663bda41 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_ExtensiblePayload.cs @@ -3,6 +3,7 @@ using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract; +using Neo.VM; using System; namespace Neo.UnitTests.Network.P2P.Payloads @@ -33,7 +34,7 @@ public void DeserializeAndSerialize() ValidBlockEnd = 789, Sender = Array.Empty().ToScriptHash(), Data = new byte[] { 1, 2, 3 }, - Witness = new Witness() { InvocationScript = new byte[] { 3, 5, 6 }, VerificationScript = Array.Empty() } + Witness = new Witness() { InvocationScript = new byte[] { (byte)OpCode.PUSH1, (byte)OpCode.PUSH2, (byte)OpCode.PUSH3 }, VerificationScript = Array.Empty() } }; var clone = test.ToArray().AsSerializable(); diff --git a/tests/neo.UnitTests/Network/P2P/UT_Message.cs b/tests/neo.UnitTests/Network/P2P/UT_Message.cs index c70e0b37fd..e688806a75 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_Message.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_Message.cs @@ -4,6 +4,7 @@ using Neo.IO; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; +using Neo.VM; using System; using System.Linq; @@ -131,7 +132,7 @@ public void Compression() Nonce = 1, Version = 0, Attributes = new TransactionAttribute[0], - Script = new byte[75], + Script = new byte[] { (byte)OpCode.PUSH1 }, Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, Witnesses = new Witness[0], }; @@ -139,13 +140,14 @@ public void Compression() var msg = Message.Create(MessageCommand.Transaction, payload); var buffer = msg.ToArray(); - buffer.Length.Should().Be(128); + buffer.Length.Should().Be(54); - payload.Script = new byte[payload.Script.Length + 10]; + payload.Script = new byte[100]; + for (int i = 0; i < payload.Script.Length; i++) payload.Script[i] = (byte)OpCode.PUSH2; msg = Message.Create(MessageCommand.Transaction, payload); buffer = msg.ToArray(); - buffer.Length.Should().Be(33); + buffer.Length.Should().Be(30); var copy = buffer.AsSerializable(); var payloadCopy = (Transaction)copy.Payload; diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index b96c06ae3c..fa6b4239db 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -45,15 +45,15 @@ public void TestToString() var context = new ContractParametersContext(tx); context.Add(contract, 0, new byte[] { 0x01 }); string str = context.ToString(); - str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hex"":""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAAQA="",""items"":{}}"); + str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hex"":""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI="",""items"":{}}"); } [TestMethod] public void TestParse() { - var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAAQA=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}]}}}"); + var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}]}}}"); ret.ScriptHashes[0].ToString().Should().Be("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); - ((Transaction)ret.Verifiable).Script.ToHexString().Should().Be(new byte[1].ToHexString()); + ((Transaction)ret.Verifiable).Script.ToHexString().Should().Be(new byte[] { 18 }.ToHexString()); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 970f29720a..c3d3209588 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -151,7 +151,7 @@ public void TestVerifyWitnesses() Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } }; BlocksAdd(snapshot2, index2, block2); - Header header2 = new Header() { PrevHash = index2, Witness = new Witness { VerificationScript = new byte[0] } }; + Header header2 = new Header() { PrevHash = index2, Witness = new Witness { InvocationScript = new byte[0], VerificationScript = new byte[0] } }; snapshot2.AddContract(UInt160.Zero, new ContractState()); snapshot2.DeleteContract(UInt160.Zero); diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index f68e058d01..d68ba3a93f 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -103,7 +103,7 @@ public static Transaction GetTransaction(UInt160 sender) { return new Transaction { - Script = new byte[1], + Script = new byte[] { (byte)OpCode.PUSH2 }, Attributes = Array.Empty(), Signers = new[]{ new Signer() { From 87856b9c63080e2a0a09476fabbe2abb8ddf2768 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 28 Jan 2021 18:42:22 +0100 Subject: [PATCH 59/65] Fix first contract id (#2277) --- src/neo/SmartContract/Native/ContractManagement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 9d61edef62..5f36991b61 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -84,7 +84,7 @@ private int GetNextAvailableId(DataCache snapshot) internal override void Initialize(ApplicationEngine engine) { engine.Snapshot.Add(CreateStorageKey(Prefix_MinimumDeploymentFee), new StorageItem(10_00000000)); - engine.Snapshot.Add(CreateStorageKey(Prefix_NextAvailableId), new StorageItem(0)); + engine.Snapshot.Add(CreateStorageKey(Prefix_NextAvailableId), new StorageItem(1)); } internal override void OnPersist(ApplicationEngine engine) From f503027356d553681aa211afb012b4c314a9f4b8 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 28 Jan 2021 22:37:10 +0100 Subject: [PATCH 60/65] Fix tx attributes (#2276) * Fix tx attributes * Optimize * Optimize * Remove reflection Co-authored-by: Erik Zhang --- src/neo/Network/P2P/Payloads/Transaction.cs | 11 ++++---- .../Network/P2P/Payloads/UT_Transaction.cs | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 5d8ffa4b3a..5ec7297fb2 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -220,14 +220,15 @@ void IInteroperable.FromStackItem(StackItem stackItem) public T GetAttribute() where T : TransactionAttribute { - return GetAttributes()?.First(); + return GetAttributes().FirstOrDefault(); } - public T[] GetAttributes() where T : TransactionAttribute + public IEnumerable GetAttributes() where T : TransactionAttribute { - _attributesCache ??= attributes.GroupBy(p => p.GetType()).ToDictionary(p => p.Key, p => (TransactionAttribute[])p.OfType().ToArray()); - _attributesCache.TryGetValue(typeof(T), out var result); - return (T[])result; + _attributesCache ??= attributes.GroupBy(p => p.GetType()).ToDictionary(p => p.Key, p => p.ToArray()); + if (_attributesCache.TryGetValue(typeof(T), out var result)) + return result.OfType(); + return Enumerable.Empty(); } public override int GetHashCode() diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 77d4d5f750..d9e4cab9b8 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -1072,6 +1072,31 @@ public void ToJson() jObj["sysfee"].AsString().Should().Be("4200000000"); } + [TestMethod] + public void Test_GetAttribute() + { + var tx = new Transaction() + { + Attributes = Array.Empty(), + NetworkFee = 0, + Nonce = (uint)Environment.TickCount, + Script = new byte[Transaction.MaxTransactionSize], + Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, + SystemFee = 0, + ValidUntilBlock = 0, + Version = 0, + Witnesses = new Witness[0], + }; + + Assert.IsNull(tx.GetAttribute()); + Assert.IsNull(tx.GetAttribute()); + + tx.Attributes = new TransactionAttribute[] { new HighPriorityAttribute() }; + + Assert.IsNull(tx.GetAttribute()); + Assert.IsNotNull(tx.GetAttribute()); + } + [TestMethod] public void Test_VerifyStateIndependent() { From b9dfe526f00df2f5f4a617c53481ed3bd0051d85 Mon Sep 17 00:00:00 2001 From: cn1010 Date: Fri, 29 Jan 2021 20:56:32 +0800 Subject: [PATCH 61/65] Add test mode gas (#2278) * add test mode gas * fix conflict * rename to maxGas * restart ut --- src/neo/Wallets/Wallet.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index b39bc6317a..788e0c8e16 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -310,7 +310,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null return MakeTransaction(snapshot, script, cosignerList.Values.ToArray(), Array.Empty(), balances_gas); } - public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Signer[] cosigners = null, TransactionAttribute[] attributes = null) + public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Signer[] cosigners = null, TransactionAttribute[] attributes = null, long maxGas = ApplicationEngine.TestModeGas) { UInt160[] accounts; if (sender is null) @@ -323,10 +323,10 @@ public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Signer[ } DataCache snapshot = Blockchain.Singleton.View; var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); - return MakeTransaction(snapshot, script, cosigners ?? Array.Empty(), attributes ?? Array.Empty(), balances_gas); + return MakeTransaction(snapshot, script, cosigners ?? Array.Empty(), attributes ?? Array.Empty(), balances_gas, maxGas); } - private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] cosigners, TransactionAttribute[] attributes, List<(UInt160 Account, BigInteger Value)> balances_gas) + private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] cosigners, TransactionAttribute[] attributes, List<(UInt160 Account, BigInteger Value)> balances_gas, long maxGas = ApplicationEngine.TestModeGas) { Random rand = new Random(); foreach (var (account, value) in balances_gas) @@ -341,8 +341,8 @@ private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] Attributes = attributes, }; - // will try to execute 'transfer' script to check if it works - using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.CreateSnapshot(), tx)) + // will try to execute 'transfer' script to check if it works + using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.CreateSnapshot(), tx, gas: maxGas)) { if (engine.State == VMState.FAULT) { From 6285ee19348b42100a38f72fab62a2e661fe3f64 Mon Sep 17 00:00:00 2001 From: cn1010 Date: Tue, 2 Feb 2021 16:41:36 +0800 Subject: [PATCH 62/65] Fix tokens in nns (#2286) * fix-tokens * protected Co-authored-by: Erik Zhang --- src/neo/SmartContract/Native/NonfungibleToken.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index c572a3889d..fb1a560b67 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -123,14 +123,14 @@ public BigInteger BalanceOf(DataCache snapshot, UInt160 owner) } [ContractMethod(0_01000000, CallFlags.ReadStates)] - private IIterator Tokens(DataCache snapshot) + protected IIterator Tokens(DataCache snapshot) { - var results = snapshot.Find(new[] { Prefix_Token }).GetEnumerator(); + var results = snapshot.Find(CreateStorageKey(Prefix_Token).ToArray()).GetEnumerator(); return new StorageIterator(results, FindOptions.ValuesOnly | FindOptions.DeserializeValues | FindOptions.PickField1, null); } [ContractMethod(0_01000000, CallFlags.ReadStates)] - private IIterator TokensOf(DataCache snapshot, UInt160 owner) + protected IIterator TokensOf(DataCache snapshot, UInt160 owner) { NFTAccountState account = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable(); IReadOnlyList tokens = account?.Tokens ?? (IReadOnlyList)System.Array.Empty(); From bbb458dbb4c64e9fc6070c42d2baa3b31c4c872c Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 2 Feb 2021 18:59:24 +0800 Subject: [PATCH 63/65] onNEP[*]Payment (#2287) --- src/neo/SmartContract/Native/FungibleToken.cs | 4 ++-- src/neo/SmartContract/Native/NonfungibleToken.cs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Native/FungibleToken.cs b/src/neo/SmartContract/Native/FungibleToken.cs index fa0e9d446a..30f931c2c1 100644 --- a/src/neo/SmartContract/Native/FungibleToken.cs +++ b/src/neo/SmartContract/Native/FungibleToken.cs @@ -161,9 +161,9 @@ private void PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, Bi if (!callOnPayment || to is null || ContractManagement.GetContract(engine.Snapshot, to) is null) return; - // Call onPayment method (NEP-17) + // Call onNEP17Payment method - engine.CallFromNativeContract(Hash, to, "onPayment", from?.ToArray() ?? StackItem.Null, amount, data); + engine.CallFromNativeContract(Hash, to, "onNEP17Payment", from?.ToArray() ?? StackItem.Null, amount, data); } } } diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index fb1a560b67..33f039cf1b 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -172,6 +172,9 @@ private void PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, by { engine.SendNotification(Hash, "Transfer", new Array { from?.ToArray() ?? StackItem.Null, to?.ToArray() ?? StackItem.Null, 1, tokenId }); + + if (to is not null && ContractManagement.GetContract(engine.Snapshot, to) is not null) + engine.CallFromNativeContract(Hash, to, "onNEP11Payment", from?.ToArray() ?? StackItem.Null, 1, tokenId); } class NFTAccountState : AccountState From 9340b300b8d3773b98b40d7e4c953f65c0a8edc7 Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Wed, 3 Feb 2021 13:13:31 +0800 Subject: [PATCH 64/65] Add header back (#2259) --- src/neo/IO/Caching/IndexedQueue.cs | 233 ++++++++++++++ src/neo/Ledger/Blockchain.cs | 82 ++++- src/neo/Ledger/HeaderCache.cs | 102 +++++++ src/neo/Network/P2P/MessageCommand.cs | 1 + src/neo/Network/P2P/Payloads/BlockBase.cs | 7 +- .../Network/P2P/Payloads/HeadersPayload.cs | 2 + .../Network/P2P/RemoteNode.ProtocolHandler.cs | 33 +- src/neo/Network/P2P/RemoteNode.cs | 7 +- src/neo/Network/P2P/TaskManager.cs | 288 ++++++++++-------- src/neo/Network/P2P/TaskSession.cs | 12 +- src/neo/Persistence/IReadOnlyStore.cs | 1 - src/neo/Persistence/MemorySnapshot.cs | 1 - src/neo/Persistence/MemoryStore.cs | 1 - .../Iterators/StorageIterator.cs | 1 - src/neo/SmartContract/KeyBuilder.cs | 1 - .../IO/Caching/UT_IndexedQueue.cs | 98 ++++++ .../Network/P2P/UT_TaskSession.cs | 16 +- .../Persistence/UT_MemoryStore.cs | 1 - .../Iterators/UT_StorageIterator.cs | 1 - tests/neo.UnitTests/UT_DataCache.cs | 1 - 20 files changed, 718 insertions(+), 171 deletions(-) create mode 100644 src/neo/IO/Caching/IndexedQueue.cs create mode 100644 src/neo/Ledger/HeaderCache.cs create mode 100644 tests/neo.UnitTests/IO/Caching/UT_IndexedQueue.cs diff --git a/src/neo/IO/Caching/IndexedQueue.cs b/src/neo/IO/Caching/IndexedQueue.cs new file mode 100644 index 0000000000..b01c6ffea9 --- /dev/null +++ b/src/neo/IO/Caching/IndexedQueue.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.IO.Caching +{ + /// + /// Represents a queue with indexed access to the items + /// + /// The type of items in the queue + class IndexedQueue : IReadOnlyCollection + { + private const int DefaultCapacity = 16; + private const int GrowthFactor = 2; + private const float TrimThreshold = 0.9f; + + private T[] _array; + private int _head; + private int _count; + + /// + /// Indicates the count of items in the queue + /// + public int Count => _count; + + /// + /// Creates a queue with the default capacity + /// + public IndexedQueue() : this(DefaultCapacity) + { + } + + /// + /// Creates a queue with the specified capacity + /// + /// The initial capacity of the queue + public IndexedQueue(int capacity) + { + if (capacity <= 0) + throw new ArgumentOutOfRangeException(nameof(capacity), "The capacity must be greater than zero."); + _array = new T[capacity]; + _head = 0; + _count = 0; + } + + /// + /// Creates a queue filled with the specified items + /// + /// The collection of items to fill the queue with + public IndexedQueue(IEnumerable collection) + { + _array = collection.ToArray(); + _head = 0; + _count = _array.Length; + } + + /// + /// Gets the value at the index + /// + /// The index + /// The value at the specified index + public ref T this[int index] + { + get + { + if (index < 0 || index >= _count) + throw new IndexOutOfRangeException(); + return ref _array[(index + _head) % _array.Length]; + } + } + + /// + /// Inserts an item at the rear of the queue + /// + /// The item to insert + public void Enqueue(T item) + { + if (_array.Length == _count) + { + int newSize = _array.Length * GrowthFactor; + if (_head == 0) + { + Array.Resize(ref _array, newSize); + } + else + { + T[] buffer = new T[newSize]; + Array.Copy(_array, _head, buffer, 0, _array.Length - _head); + Array.Copy(_array, 0, buffer, _array.Length - _head, _head); + _array = buffer; + _head = 0; + } + } + _array[(_head + _count) % _array.Length] = item; + ++_count; + } + + /// + /// Provides access to the item at the front of the queue without dequeueing it + /// + /// The frontmost item + public T Peek() + { + if (_count == 0) + throw new InvalidOperationException("The queue is empty."); + return _array[_head]; + } + + /// + /// Attempts to return an item from the front of the queue without removing it + /// + /// The item + /// True if the queue returned an item or false if the queue is empty + public bool TryPeek(out T item) + { + if (_count == 0) + { + item = default; + return false; + } + else + { + item = _array[_head]; + return true; + } + } + + /// + /// Removes an item from the front of the queue, returning it + /// + /// The item that was removed + public T Dequeue() + { + if (_count == 0) + throw new InvalidOperationException("The queue is empty"); + T result = _array[_head]; + ++_head; + _head %= _array.Length; + --_count; + return result; + } + + /// + /// Attempts to return an item from the front of the queue, removing it + /// + /// The item + /// True if the queue returned an item or false if the queue is empty + public bool TryDequeue(out T item) + { + if (_count == 0) + { + item = default; + return false; + } + else + { + item = _array[_head]; + ++_head; + _head %= _array.Length; + --_count; + return true; + } + } + + /// + /// Clears the items from the queue + /// + public void Clear() + { + _head = 0; + _count = 0; + } + + /// + /// Trims the extra array space that isn't being used. + /// + public void TrimExcess() + { + if (_count == 0) + { + _array = new T[DefaultCapacity]; + } + else if (_array.Length * TrimThreshold >= _count) + { + T[] arr = new T[_count]; + CopyTo(arr, 0); + _array = arr; + _head = 0; + } + } + + /// + /// Copys the queue's items to a destination array + /// + /// The destination array + /// The index in the destination to start copying at + public void CopyTo(T[] array, int arrayIndex) + { + if (array is null) throw new ArgumentNullException(nameof(array)); + if (arrayIndex < 0 || arrayIndex + _count > array.Length) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + if (_head + _count <= _array.Length) + { + Array.Copy(_array, _head, array, arrayIndex, _count); + } + else + { + Array.Copy(_array, _head, array, arrayIndex, _array.Length - _head); + Array.Copy(_array, 0, array, arrayIndex + _array.Length - _head, _count + _head - _array.Length); + } + } + + /// + /// Returns an array of the items in the queue + /// + /// An array containing the queue's items + public T[] ToArray() + { + T[] result = new T[_count]; + CopyTo(result, 0); + return result; + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < _count; i++) + yield return _array[(_head + i) % _array.Length]; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 8a3ac9c6d4..8a923e87d0 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -16,6 +16,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Threading; @@ -74,6 +75,7 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu ///
public DataCache View => new SnapshotCache(Store); public MemoryPool MemPool { get; } + public HeaderCache HeaderCache { get; } = new HeaderCache(); private static Blockchain singleton; public static Blockchain Singleton @@ -124,6 +126,12 @@ public Blockchain(NeoSystem system, IStore store) } } + protected override void PostStop() + { + base.PostStop(); + HeaderCache.Dispose(); + } + private bool ContainsTransaction(UInt256 hash) { if (MemPool.ContainsKey(hash)) return true; @@ -221,32 +229,84 @@ private VerifyResult OnNewBlock(Block block) { DataCache snapshot = View; uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); + uint headerHeight = HeaderCache.Last?.Index ?? NativeContract.Ledger.CurrentIndex(snapshot); if (block.Index <= currentHeight) return VerifyResult.AlreadyExists; - if (block.Index - 1 > currentHeight) + if (block.Index - 1 > headerHeight) { AddUnverifiedBlockToCache(block); return VerifyResult.UnableToVerify; } - if (block.Index == currentHeight + 1) + if (block.Index == headerHeight + 1) { if (!block.Verify(snapshot)) return VerifyResult.Invalid; - block_cache.TryAdd(block.Hash, block); - block_cache_unverified.Remove(block.Index); - Persist(block); - if (block_cache_unverified.TryGetValue(block.Index + 1, out var unverifiedBlocks)) + } + else + { + if (!block.Hash.Equals(HeaderCache[block.Index].Hash)) + return VerifyResult.Invalid; + } + block_cache.TryAdd(block.Hash, block); + if (block.Index == currentHeight + 1) + { + Block block_persist = block; + List blocksToPersistList = new List(); + while (true) + { + blocksToPersistList.Add(block_persist); + if (block_persist.Index + 1 > headerHeight) break; + UInt256 hash = HeaderCache[block_persist.Index + 1].Hash; + if (!block_cache.TryGetValue(hash, out block_persist)) break; + } + + int blocksPersisted = 0; + // 15000 is the default among of seconds per block, while MilliSecondsPerBlock is the current + uint extraBlocks = (15000 - MillisecondsPerBlock) / 1000; + foreach (Block blockToPersist in blocksToPersistList) + { + block_cache_unverified.Remove(blockToPersist.Index); + Persist(blockToPersist); + + if (blocksPersisted++ < blocksToPersistList.Count - (2 + Math.Max(0, extraBlocks))) continue; + // Empirically calibrated for relaying the most recent 2 blocks persisted with 15s network + // Increase in the rate of 1 block per second in configurations with faster blocks + + if (blockToPersist.Index + 99 >= headerHeight) + system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = blockToPersist }); + } + if (block_cache_unverified.TryGetValue(currentHeight + 1, out var unverifiedBlocks)) { foreach (var unverifiedBlock in unverifiedBlocks.Blocks) Self.Tell(unverifiedBlock, ActorRefs.NoSender); block_cache_unverified.Remove(block.Index + 1); } - // We can store the new block in block_cache and tell the new height to other nodes after Persist(). - system.LocalNode.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(block.Index))); + } + else + { + if (block.Index + 99 >= headerHeight) + system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = block }); + if (block.Index == headerHeight + 1) + HeaderCache.Add(block.Header); } return VerifyResult.Succeed; } + private void OnNewHeaders(Header[] headers) + { + if (HeaderCache.Full) return; + DataCache snapshot = View; + uint headerHeight = HeaderCache.Last?.Index ?? NativeContract.Ledger.CurrentIndex(snapshot); + foreach (Header header in headers) + { + if (header.Index > headerHeight + 1) break; + if (header.Index < headerHeight + 1) continue; + if (!header.Verify(snapshot)) break; + HeaderCache.Add(header); + ++headerHeight; + } + } + private VerifyResult OnNewInventory(IInventory inventory) { if (!inventory.Verify(View)) return VerifyResult.Invalid; @@ -278,6 +338,9 @@ protected override void OnReceive(object message) case FillMemoryPool fill: OnFillMemoryPool(fill.Transactions); break; + case Header[] headers: + OnNewHeaders(headers); + break; case Block block: OnInventory(block, false); break; @@ -373,6 +436,8 @@ private void Persist(Block block) } block_cache.TryRemove(block.PrevHash, out _); Context.System.EventStream.Publish(new PersistCompleted { Block = block }); + if (HeaderCache.TryRemoveFirst(out Header header)) + Debug.Assert(header.Index == block.Index); } public static Props Props(NeoSystem system, IStore store) @@ -431,6 +496,7 @@ internal protected override bool IsHighPriority(object message) { switch (message) { + case Header[] _: case Block _: case ExtensiblePayload _: case Terminated _: diff --git a/src/neo/Ledger/HeaderCache.cs b/src/neo/Ledger/HeaderCache.cs new file mode 100644 index 0000000000..ea3380baa9 --- /dev/null +++ b/src/neo/Ledger/HeaderCache.cs @@ -0,0 +1,102 @@ +using Neo.IO.Caching; +using Neo.Network.P2P.Payloads; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace Neo.Ledger +{ + public sealed class HeaderCache : IDisposable, IEnumerable
+ { + private readonly IndexedQueue
headers = new IndexedQueue
(); + private readonly ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim(); + + public Header this[uint index] + { + get + { + readerWriterLock.EnterReadLock(); + try + { + if (headers.Count == 0) return null; + uint firstIndex = headers[0].Index; + if (index < firstIndex) return null; + index -= firstIndex; + if (index >= headers.Count) return null; + return headers[(int)index]; + } + finally + { + readerWriterLock.ExitReadLock(); + } + } + } + + public bool Full => headers.Count >= 10000; + + public Header Last + { + get + { + readerWriterLock.EnterReadLock(); + try + { + if (headers.Count == 0) return null; + return headers[^1]; + } + finally + { + readerWriterLock.ExitReadLock(); + } + } + } + + public void Dispose() + { + readerWriterLock.Dispose(); + } + + internal void Add(Header header) + { + readerWriterLock.EnterWriteLock(); + try + { + headers.Enqueue(header); + } + finally + { + readerWriterLock.ExitWriteLock(); + } + } + + internal bool TryRemoveFirst(out Header header) + { + readerWriterLock.EnterWriteLock(); + try + { + return headers.TryDequeue(out header); + } + finally + { + readerWriterLock.ExitWriteLock(); + } + } + + public IEnumerator
GetEnumerator() + { + readerWriterLock.EnterReadLock(); + try + { + foreach (Header header in headers) + yield return header; + } + finally + { + readerWriterLock.ExitReadLock(); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index 04856c4a48..c66c15799a 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -33,6 +33,7 @@ public enum MessageCommand : byte GetData = 0x28, [ReflectionCache(typeof(GetBlockByIndexPayload))] GetBlockByIndex = 0x29, + [ReflectionCache(typeof(InvPayload))] NotFound = 0x2a, [ReflectionCache(typeof(Transaction))] Transaction = 0x2b, diff --git a/src/neo/Network/P2P/Payloads/BlockBase.cs b/src/neo/Network/P2P/Payloads/BlockBase.cs index 08b2657295..a073858cf6 100644 --- a/src/neo/Network/P2P/Payloads/BlockBase.cs +++ b/src/neo/Network/P2P/Payloads/BlockBase.cs @@ -1,5 +1,6 @@ using Neo.IO; using Neo.IO.Json; +using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -76,7 +77,7 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) { if (PrevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; - TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); + BlockBase prev = Blockchain.Singleton.HeaderCache[Index - 1] ?? (BlockBase)NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); if (prev is null) throw new InvalidOperationException(); return new[] { prev.NextConsensus }; } @@ -114,9 +115,9 @@ public virtual JObject ToJson() public virtual bool Verify(DataCache snapshot) { - TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); + var prev = Blockchain.Singleton.HeaderCache[Index - 1] ?? NativeContract.Ledger.GetHeader(snapshot, Index - 1); if (prev is null) return false; - if (prev.Index + 1 != Index) return false; + if (prev.Hash != PrevHash) return false; if (prev.Timestamp >= Timestamp) return false; if (!this.VerifyWitnesses(snapshot, 1_00000000)) return false; return true; diff --git a/src/neo/Network/P2P/Payloads/HeadersPayload.cs b/src/neo/Network/P2P/Payloads/HeadersPayload.cs index 2e95d4ebf4..9251a7f5a1 100644 --- a/src/neo/Network/P2P/Payloads/HeadersPayload.cs +++ b/src/neo/Network/P2P/Payloads/HeadersPayload.cs @@ -1,4 +1,5 @@ using Neo.IO; +using System; using System.IO; namespace Neo.Network.P2P.Payloads @@ -22,6 +23,7 @@ public static HeadersPayload Create(params Header[] headers) void ISerializable.Deserialize(BinaryReader reader) { Headers = reader.ReadSerializableArray
(MaxHeadersCount); + if (Headers.Length == 0) throw new FormatException(); } void ISerializable.Serialize(BinaryWriter writer) diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index fb9fd5cc5b..9c3749d415 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -34,8 +34,6 @@ protected override UInt256 GetKeyForItem((UInt256, DateTime) item) private BloomFilter bloom_filter; private static readonly TimeSpan TimerInterval = TimeSpan.FromSeconds(30); - private static readonly TimeSpan PendingTimeout = TimeSpan.FromMinutes(1); - private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender); private void OnMessage(Message msg) @@ -90,6 +88,9 @@ private void OnMessage(Message msg) case MessageCommand.GetHeaders: OnGetHeadersMessageReceived((GetBlockByIndexPayload)msg.Payload); break; + case MessageCommand.Headers: + OnHeadersMessageReceived((HeadersPayload)msg.Payload); + break; case MessageCommand.Inv: OnInvMessageReceived((InvPayload)msg.Payload); break; @@ -110,7 +111,6 @@ private void OnMessage(Message msg) case MessageCommand.Version: throw new ProtocolViolationException(); case MessageCommand.Alert: - case MessageCommand.Headers: case MessageCommand.MerkleBlock: case MessageCommand.NotFound: case MessageCommand.Reject: @@ -269,7 +269,7 @@ private void OnGetDataMessageReceived(InvPayload payload) /// Tell the specified number of blocks' headers starting with the requested IndexStart to RemoteNode actor. /// A limit set by HeadersPayload.MaxHeadersCount is also applied to the number of requested Headers, namely payload.Count. ///
- /// A GetBlocksPayload including start block index and number of blocks' headers requested. + /// A GetBlockByIndexPayload including start block index and number of blocks' headers requested. private void OnGetHeadersMessageReceived(GetBlockByIndexPayload payload) { DataCache snapshot = Blockchain.Singleton.View; @@ -286,13 +286,20 @@ private void OnGetHeadersMessageReceived(GetBlockByIndexPayload payload) EnqueueMessage(Message.Create(MessageCommand.Headers, HeadersPayload.Create(headers.ToArray()))); } + private void OnHeadersMessageReceived(HeadersPayload payload) + { + UpdateLastBlockIndex(payload.Headers[^1].Index); + system.TaskManager.Tell(payload.Headers); + system.Blockchain.Tell(payload.Headers); + } + private void OnInventoryReceived(IInventory inventory) { pendingKnownHashes.Remove(inventory.Hash); if (inventory is Block block) { + UpdateLastBlockIndex(block.Index); if (block.Index > NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View) + InvPayload.MaxHashesCount) return; - UpdateLastBlockIndex(block.Index, false); } knownHashes.Add(inventory.Hash); system.TaskManager.Tell(inventory); @@ -332,13 +339,13 @@ private void OnMemPoolMessageReceived() private void OnPingMessageReceived(PingPayload payload) { - UpdateLastBlockIndex(payload.LastBlockIndex, true); + UpdateLastBlockIndex(payload.LastBlockIndex); EnqueueMessage(Message.Create(MessageCommand.Pong, PingPayload.Create(NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View), payload.Nonce))); } private void OnPongMessageReceived(PingPayload payload) { - UpdateLastBlockIndex(payload.LastBlockIndex, true); + UpdateLastBlockIndex(payload.LastBlockIndex); } private void OnVerackMessageReceived() @@ -373,23 +380,25 @@ private void OnVersionMessageReceived(VersionPayload payload) SendMessage(Message.Create(MessageCommand.Verack)); } - private void RefreshPendingKnownHashes() + private void OnTimer() { + DateTime oneMinuteAgo = TimeProvider.Current.UtcNow.AddMinutes(-1); while (pendingKnownHashes.Count > 0) { var (_, time) = pendingKnownHashes[0]; - if (TimeProvider.Current.UtcNow - time <= PendingTimeout) - break; + if (oneMinuteAgo <= time) break; pendingKnownHashes.RemoveAt(0); } + if (oneMinuteAgo > lastSent) + EnqueueMessage(Message.Create(MessageCommand.Ping, PingPayload.Create(NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View)))); } - private void UpdateLastBlockIndex(uint lastBlockIndex, bool requestTasks) + private void UpdateLastBlockIndex(uint lastBlockIndex) { if (lastBlockIndex > LastBlockIndex) { LastBlockIndex = lastBlockIndex; - system.TaskManager.Tell(new TaskManager.Update { LastBlockIndex = LastBlockIndex, RequestTasks = requestTasks }); + system.TaskManager.Tell(new TaskManager.Update { LastBlockIndex = LastBlockIndex }); } } } diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index a417e4115e..dee2b893f3 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -8,6 +8,7 @@ using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -23,6 +24,7 @@ internal class Relay { public IInventory Inventory; } private readonly NeoSystem system; private readonly Queue message_queue_high = new Queue(); private readonly Queue message_queue_low = new Queue(); + private DateTime lastSent = TimeProvider.Current.UtcNow; private readonly bool[] sentCommands = new bool[1 << (sizeof(MessageCommand) * 8)]; private ByteString msg_buffer = ByteString.Empty; private bool ack = true; @@ -100,7 +102,10 @@ private void EnqueueMessage(Message message) break; } if (!is_single || message_queue.All(p => p.Command != message.Command)) + { message_queue.Enqueue(message); + lastSent = TimeProvider.Current.UtcNow; + } CheckMessageQueue(); } @@ -124,7 +129,7 @@ protected override void OnReceive(object message) switch (message) { case Timer _: - RefreshPendingKnownHashes(); + OnTimer(); break; case Message msg: if (msg.Payload is PingPayload payload) diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 52aa7013a5..8ea8457a72 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -1,5 +1,6 @@ using Akka.Actor; using Akka.Configuration; +using Akka.IO; using Neo.IO.Actors; using Neo.IO.Caching; using Neo.Ledger; @@ -17,95 +18,82 @@ namespace Neo.Network.P2P public class TaskManager : UntypedActor { internal class Register { public VersionPayload Version; } - internal class Update { public uint LastBlockIndex; public bool RequestTasks; } + internal class Update { public uint LastBlockIndex; } internal class NewTasks { public InvPayload Payload; } public class RestartTasks { public InvPayload Payload; } private class Timer { } private static readonly TimeSpan TimerInterval = TimeSpan.FromSeconds(30); private static readonly TimeSpan TaskTimeout = TimeSpan.FromMinutes(1); - private static readonly UInt256 MemPoolTaskHash = UInt256.Parse("0x0000000000000000000000000000000000000000000000000000000000000001"); + private static readonly UInt256 HeaderTaskHash = UInt256.Zero; private const int MaxConncurrentTasks = 3; - private const int MaxSyncTasksCount = 50; - private const int PingCoolingOffPeriod = 60_000; // in ms. private readonly NeoSystem system; /// /// A set of known hashes, of inventories or payloads, already received. /// private readonly HashSetCache knownHashes; - private readonly Dictionary globalTasks = new Dictionary(); - private readonly Dictionary receivedBlockIndex = new Dictionary(); - private readonly HashSet failedSyncTasks = new HashSet(); + private readonly Dictionary globalInvTasks = new Dictionary(); + private readonly Dictionary globalIndexTasks = new Dictionary(); private readonly Dictionary sessions = new Dictionary(); private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender); - private uint lastTaskIndex = 0; + + private bool HasHeaderTask => globalInvTasks.ContainsKey(HeaderTaskHash); public TaskManager(NeoSystem system) { this.system = system; this.knownHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); - this.lastTaskIndex = NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View); Context.System.EventStream.Subscribe(Self, typeof(Blockchain.PersistCompleted)); Context.System.EventStream.Subscribe(Self, typeof(Blockchain.RelayResult)); } - private bool AssignSyncTask(uint index, TaskSession filterSession = null) - { - if (index <= NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View) || sessions.Values.Any(p => p != filterSession && p.IndexTasks.ContainsKey(index))) - return true; - Random rand = new Random(); - KeyValuePair remoteNode = sessions.Where(p => p.Value != filterSession && p.Value.LastBlockIndex >= index) - .OrderBy(p => p.Value.IndexTasks.Count) - .ThenBy(s => rand.Next()) - .FirstOrDefault(); - if (remoteNode.Value == null) - { - failedSyncTasks.Add(index); - return false; - } - TaskSession session = remoteNode.Value; - session.IndexTasks.TryAdd(index, TimeProvider.Current.UtcNow); - remoteNode.Key.Tell(Message.Create(MessageCommand.GetBlockByIndex, GetBlockByIndexPayload.Create(index, 1))); - failedSyncTasks.Remove(index); - return true; - } - - private void OnBlock(Block block) + private void OnHeaders(Header[] _) { - var session = sessions.Values.FirstOrDefault(p => p.IndexTasks.ContainsKey(block.Index)); - if (session is null) return; - session.IndexTasks.Remove(block.Index); - receivedBlockIndex.TryAdd(block.Index, session); - RequestTasks(false); + if (!sessions.TryGetValue(Sender, out TaskSession session)) + return; + if (session.InvTasks.Remove(HeaderTaskHash)) + DecrementGlobalTask(HeaderTaskHash); + RequestTasks(Sender, session); } private void OnInvalidBlock(Block invalidBlock) { - receivedBlockIndex.TryGetValue(invalidBlock.Index, out TaskSession session); - if (session is null) return; - session.InvalidBlockCount++; - session.IndexTasks.Remove(invalidBlock.Index); - receivedBlockIndex.Remove(invalidBlock.Index); - AssignSyncTask(invalidBlock.Index, session); + foreach (var (actor, session) in sessions) + if (session.ReceivedBlock.TryGetValue(invalidBlock.Index, out Block block)) + if (block.Hash == invalidBlock.Hash) + actor.Tell(Tcp.Abort.Instance); } private void OnNewTasks(InvPayload payload) { if (!sessions.TryGetValue(Sender, out TaskSession session)) return; - // Do not accept payload of type InventoryType.TX if not synced on best known HeaderHeight - if (payload.Type == InventoryType.TX && NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View) < sessions.Values.Max(p => p.LastBlockIndex)) + + // Do not accept payload of type InventoryType.TX if not synced on HeaderHeight + uint currentHeight = NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View); + uint headerHeight = Blockchain.Singleton.HeaderCache.Last?.Index ?? currentHeight; + if (currentHeight < headerHeight && (payload.Type == InventoryType.TX || (payload.Type == InventoryType.Block && currentHeight < session.LastBlockIndex - InvPayload.MaxHashesCount))) + { + RequestTasks(Sender, session); return; + } + HashSet hashes = new HashSet(payload.Hashes); // Remove all previously processed knownHashes from the list that is being requested hashes.Remove(knownHashes); + // Add to AvailableTasks the ones, of type InventoryType.Block, that are global (already under process by other sessions) + if (payload.Type == InventoryType.Block) + session.AvailableTasks.UnionWith(hashes.Where(p => globalInvTasks.ContainsKey(p))); // Remove those that are already in process by other sessions - hashes.Remove(globalTasks); + hashes.Remove(globalInvTasks); if (hashes.Count == 0) + { + RequestTasks(Sender, session); return; + } // Update globalTasks with the ones that will be requested within this current session foreach (UInt256 hash in hashes) @@ -120,8 +108,14 @@ private void OnNewTasks(InvPayload payload) private void OnPersistCompleted(Block block) { - receivedBlockIndex.Remove(block.Index); - RequestTasks(false); + foreach (var (actor, session) in sessions) + if (session.ReceivedBlock.Remove(block.Index, out Block receivedBlock)) + { + if (block.Hash == receivedBlock.Hash) + RequestTasks(actor, session); + else + actor.Tell(Tcp.Abort.Instance); + } } protected override void OnReceive(object message) @@ -140,11 +134,11 @@ protected override void OnReceive(object message) case RestartTasks restart: OnRestartTasks(restart.Payload); break; - case Block block: - OnBlock(block); + case Header[] headers: + OnHeaders(headers); break; case IInventory inventory: - OnTaskCompleted(inventory.Hash); + OnTaskCompleted(inventory); break; case Blockchain.PersistCompleted pc: OnPersistCompleted(pc.Block); @@ -166,10 +160,8 @@ private void OnRegister(VersionPayload version) { Context.Watch(Sender); TaskSession session = new TaskSession(version); - if (session.IsFullNode) - session.InvTasks.TryAdd(MemPoolTaskHash, TimeProvider.Current.UtcNow); - sessions.TryAdd(Sender, session); - RequestTasks(true); + sessions.Add(Sender, session); + RequestTasks(Sender, session); } private void OnUpdate(Update update) @@ -177,51 +169,100 @@ private void OnUpdate(Update update) if (!sessions.TryGetValue(Sender, out TaskSession session)) return; session.LastBlockIndex = update.LastBlockIndex; - session.ExpireTime = TimeProvider.Current.UtcNow.AddMilliseconds(PingCoolingOffPeriod); - if (update.RequestTasks) RequestTasks(true); } private void OnRestartTasks(InvPayload payload) { knownHashes.ExceptWith(payload.Hashes); foreach (UInt256 hash in payload.Hashes) - globalTasks.Remove(hash); + globalInvTasks.Remove(hash); foreach (InvPayload group in InvPayload.CreateGroup(payload.Type, payload.Hashes)) system.LocalNode.Tell(Message.Create(MessageCommand.GetData, group)); } - private void OnTaskCompleted(UInt256 hash) + private void OnTaskCompleted(IInventory inventory) { - knownHashes.Add(hash); - globalTasks.Remove(hash); + Block block = inventory as Block; + knownHashes.Add(inventory.Hash); + globalInvTasks.Remove(inventory.Hash); + if (block is not null) + globalIndexTasks.Remove(block.Index); + foreach (TaskSession ms in sessions.Values) + ms.AvailableTasks.Remove(inventory.Hash); if (sessions.TryGetValue(Sender, out TaskSession session)) - session.InvTasks.Remove(hash); + { + session.InvTasks.Remove(inventory.Hash); + if (block is not null) + { + session.IndexTasks.Remove(block.Index); + if (session.ReceivedBlock.TryGetValue(block.Index, out var block_old)) + { + if (block.Hash != block_old.Hash) + { + Sender.Tell(Tcp.Abort.Instance); + return; + } + } + else + { + session.ReceivedBlock.Add(block.Index, block); + } + } + RequestTasks(Sender, session); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DecrementGlobalTask(UInt256 hash) { - if (globalTasks.TryGetValue(hash, out var value)) + if (globalInvTasks.TryGetValue(hash, out var value)) + { + if (value == 1) + globalInvTasks.Remove(hash); + else + globalInvTasks[hash] = value - 1; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DecrementGlobalTask(uint index) + { + if (globalIndexTasks.TryGetValue(index, out var value)) { if (value == 1) - globalTasks.Remove(hash); + globalIndexTasks.Remove(index); else - globalTasks[hash] = value - 1; + globalIndexTasks[index] = value - 1; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IncrementGlobalTask(UInt256 hash) { - if (!globalTasks.TryGetValue(hash, out var value)) + if (!globalInvTasks.TryGetValue(hash, out var value)) { - globalTasks[hash] = 1; + globalInvTasks[hash] = 1; return true; } if (value >= MaxConncurrentTasks) return false; - globalTasks[hash] = value + 1; + globalInvTasks[hash] = value + 1; + return true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool IncrementGlobalTask(uint index) + { + if (!globalIndexTasks.TryGetValue(index, out var value)) + { + globalIndexTasks[index] = 1; + return true; + } + if (value >= MaxConncurrentTasks) + return false; + + globalIndexTasks[index] = value + 1; return true; } @@ -229,11 +270,10 @@ private void OnTerminated(IActorRef actor) { if (!sessions.TryGetValue(actor, out TaskSession session)) return; - foreach (uint index in session.IndexTasks.Keys) - AssignSyncTask(index, session); - foreach (UInt256 hash in session.InvTasks.Keys) DecrementGlobalTask(hash); + foreach (uint index in session.IndexTasks.Keys) + DecrementGlobalTask(index); sessions.Remove(actor); } @@ -241,26 +281,21 @@ private void OnTimer() { foreach (TaskSession session in sessions.Values) { - foreach (KeyValuePair kvp in session.IndexTasks) - { - if (TimeProvider.Current.UtcNow - kvp.Value > TaskTimeout) + foreach (var (hash, time) in session.InvTasks.ToArray()) + if (TimeProvider.Current.UtcNow - time > TaskTimeout) { - session.IndexTasks.Remove(kvp.Key); - session.TimeoutTimes++; - AssignSyncTask(kvp.Key, session); + if (session.InvTasks.Remove(hash)) + DecrementGlobalTask(hash); } - } - - foreach (var task in session.InvTasks.ToArray()) - { - if (TimeProvider.Current.UtcNow - task.Value > TaskTimeout) + foreach (var (index, time) in session.IndexTasks.ToArray()) + if (TimeProvider.Current.UtcNow - time > TaskTimeout) { - if (session.InvTasks.Remove(task.Key)) - DecrementGlobalTask(task.Key); + if (session.IndexTasks.Remove(index)) + DecrementGlobalTask(index); } - } } - RequestTasks(true); + foreach (var (actor, session) in sessions) + RequestTasks(actor, session); } protected override void PostStop() @@ -274,56 +309,64 @@ public static Props Props(NeoSystem system) return Akka.Actor.Props.Create(() => new TaskManager(system)).WithMailbox("task-manager-mailbox"); } - private void RequestTasks(bool sendPing) + private void RequestTasks(IActorRef remoteNode, TaskSession session) { - if (sessions.Count == 0) return; - - if (sendPing) SendPingMessage(); + if (session.HasTooManyTasks) return; - uint currentHeight = NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View); + DataCache snapshot = Blockchain.Singleton.View; - while (failedSyncTasks.Count > 0) + // If there are pending tasks of InventoryType.Block we should process them + if (session.AvailableTasks.Count > 0) { - var failedTask = failedSyncTasks.First(); - if (failedTask <= currentHeight) + session.AvailableTasks.Remove(knownHashes); + // Search any similar hash that is on Singleton's knowledge, which means, on the way or already processed + session.AvailableTasks.RemoveWhere(p => NativeContract.Ledger.ContainsBlock(snapshot, p)); + HashSet hashes = new HashSet(session.AvailableTasks); + if (hashes.Count > 0) { - failedSyncTasks.Remove(failedTask); - continue; + foreach (UInt256 hash in hashes.ToArray()) + { + if (!IncrementGlobalTask(hash)) + hashes.Remove(hash); + } + session.AvailableTasks.Remove(hashes); + foreach (UInt256 hash in hashes) + session.InvTasks[hash] = DateTime.UtcNow; + foreach (InvPayload group in InvPayload.CreateGroup(InventoryType.Block, hashes.ToArray())) + remoteNode.Tell(Message.Create(MessageCommand.GetData, group)); + return; } - if (!AssignSyncTask(failedTask)) return; } - int taskCounts = sessions.Values.Sum(p => p.IndexTasks.Count); - var highestBlockIndex = sessions.Values.Max(p => p.LastBlockIndex); - for (; taskCounts < MaxSyncTasksCount; taskCounts++) + uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); + uint headerHeight = Blockchain.Singleton.HeaderCache.Last?.Index ?? currentHeight; + // When the number of AvailableTasks is no more than 0, no pending tasks of InventoryType.Block, it should process pending the tasks of headers + // If not HeaderTask pending to be processed it should ask for more Blocks + if ((!HasHeaderTask || globalInvTasks[HeaderTaskHash] < MaxConncurrentTasks) && headerHeight < session.LastBlockIndex && !Blockchain.Singleton.HeaderCache.Full) { - if (lastTaskIndex >= highestBlockIndex || lastTaskIndex >= currentHeight + InvPayload.MaxHashesCount) break; - if (!AssignSyncTask(++lastTaskIndex)) break; + session.InvTasks[HeaderTaskHash] = DateTime.UtcNow; + IncrementGlobalTask(HeaderTaskHash); + remoteNode.Tell(Message.Create(MessageCommand.GetHeaders, GetBlockByIndexPayload.Create(headerHeight))); } - } - - private void SendPingMessage() - { - DataCache snapshot = Blockchain.Singleton.View; - uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); - UInt256 currentHash = NativeContract.Ledger.CurrentHash(snapshot); - TrimmedBlock block = NativeContract.Ledger.GetTrimmedBlock(snapshot, currentHash); - foreach (KeyValuePair item in sessions) + else if (currentHeight < session.LastBlockIndex) { - var node = item.Key; - var session = item.Value; - - if (session.ExpireTime < TimeProvider.Current.UtcNow || - (block.Index >= session.LastBlockIndex && - TimeProvider.Current.UtcNow.ToTimestampMS() - PingCoolingOffPeriod >= block.Timestamp)) + uint startHeight = currentHeight; + while (globalIndexTasks.ContainsKey(++startHeight)) { } + if (startHeight > session.LastBlockIndex || startHeight >= currentHeight + InvPayload.MaxHashesCount) return; + uint endHeight = startHeight; + while (!globalIndexTasks.ContainsKey(++endHeight) && endHeight <= session.LastBlockIndex && endHeight <= currentHeight + InvPayload.MaxHashesCount) { } + uint count = Math.Min(endHeight - startHeight, InvPayload.MaxHashesCount); + for (uint i = 0; i < count; i++) { - if (session.InvTasks.Remove(MemPoolTaskHash)) - { - node.Tell(Message.Create(MessageCommand.Mempool)); - } - node.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(currentHeight))); - session.ExpireTime = TimeProvider.Current.UtcNow.AddMilliseconds(PingCoolingOffPeriod); + session.IndexTasks[startHeight + i] = TimeProvider.Current.UtcNow; + IncrementGlobalTask(startHeight + i); } + remoteNode.Tell(Message.Create(MessageCommand.GetBlockByIndex, GetBlockByIndexPayload.Create(startHeight, (short)count))); + } + else if (!session.MempoolSent) + { + session.MempoolSent = true; + remoteNode.Tell(Message.Create(MessageCommand.Mempool)); } } } @@ -340,6 +383,7 @@ internal protected override bool IsHighPriority(object message) switch (message) { case TaskManager.Register _: + case TaskManager.Update _: case TaskManager.RestartTasks _: return true; case TaskManager.NewTasks tasks: diff --git a/src/neo/Network/P2P/TaskSession.cs b/src/neo/Network/P2P/TaskSession.cs index b8a78bd5f6..6764789b24 100644 --- a/src/neo/Network/P2P/TaskSession.cs +++ b/src/neo/Network/P2P/TaskSession.cs @@ -8,14 +8,14 @@ namespace Neo.Network.P2P { internal class TaskSession { - public readonly Dictionary InvTasks = new Dictionary(); - public readonly Dictionary IndexTasks = new Dictionary(); - + public Dictionary InvTasks { get; } = new Dictionary(); + public Dictionary IndexTasks { get; } = new Dictionary(); + public HashSet AvailableTasks { get; } = new HashSet(); + public Dictionary ReceivedBlock { get; } = new Dictionary(); + public bool HasTooManyTasks => InvTasks.Count + IndexTasks.Count >= 100; public bool IsFullNode { get; } public uint LastBlockIndex { get; set; } - public uint TimeoutTimes = 0; - public uint InvalidBlockCount = 0; - public DateTime ExpireTime = DateTime.MinValue; + public bool MempoolSent { get; set; } public TaskSession(VersionPayload version) { diff --git a/src/neo/Persistence/IReadOnlyStore.cs b/src/neo/Persistence/IReadOnlyStore.cs index f13fd56d7f..7200691084 100644 --- a/src/neo/Persistence/IReadOnlyStore.cs +++ b/src/neo/Persistence/IReadOnlyStore.cs @@ -1,4 +1,3 @@ -using Neo.IO.Caching; using System.Collections.Generic; namespace Neo.Persistence diff --git a/src/neo/Persistence/MemorySnapshot.cs b/src/neo/Persistence/MemorySnapshot.cs index 7b3c77dbe8..9c2ee79998 100644 --- a/src/neo/Persistence/MemorySnapshot.cs +++ b/src/neo/Persistence/MemorySnapshot.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.IO.Caching; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/neo/Persistence/MemoryStore.cs b/src/neo/Persistence/MemoryStore.cs index 705f509b87..1cf51dde51 100644 --- a/src/neo/Persistence/MemoryStore.cs +++ b/src/neo/Persistence/MemoryStore.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.IO.Caching; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; diff --git a/src/neo/SmartContract/Iterators/StorageIterator.cs b/src/neo/SmartContract/Iterators/StorageIterator.cs index bcca51b471..74dc5015cc 100644 --- a/src/neo/SmartContract/Iterators/StorageIterator.cs +++ b/src/neo/SmartContract/Iterators/StorageIterator.cs @@ -1,4 +1,3 @@ -using Neo.Ledger; using Neo.VM; using Neo.VM.Types; using System.Collections.Generic; diff --git a/src/neo/SmartContract/KeyBuilder.cs b/src/neo/SmartContract/KeyBuilder.cs index c725da370b..80bea0e783 100644 --- a/src/neo/SmartContract/KeyBuilder.cs +++ b/src/neo/SmartContract/KeyBuilder.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Ledger; using System; using System.IO; diff --git a/tests/neo.UnitTests/IO/Caching/UT_IndexedQueue.cs b/tests/neo.UnitTests/IO/Caching/UT_IndexedQueue.cs new file mode 100644 index 0000000000..e09726924c --- /dev/null +++ b/tests/neo.UnitTests/IO/Caching/UT_IndexedQueue.cs @@ -0,0 +1,98 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Caching; +using System; +using System.Linq; + +namespace Neo.UnitTests.IO.Caching +{ + [TestClass] + public class UT_IndexedQueue + { + [TestMethod] + public void TestDefault() + { + var queue = new IndexedQueue(10); + queue.Count.Should().Be(0); + + queue = new IndexedQueue(); + queue.Count.Should().Be(0); + queue.TrimExcess(); + queue.Count.Should().Be(0); + + queue = new IndexedQueue(Array.Empty()); + queue.Count.Should().Be(0); + queue.TryPeek(out var a).Should().BeFalse(); + a.Should().Be(0); + queue.TryDequeue(out a).Should().BeFalse(); + a.Should().Be(0); + + Assert.ThrowsException(() => queue.Peek()); + Assert.ThrowsException(() => queue.Dequeue()); + Assert.ThrowsException(() => _ = queue[-1]); + Assert.ThrowsException(() => queue[-1] = 1); + Assert.ThrowsException(() => _ = queue[1]); + Assert.ThrowsException(() => queue[1] = 1); + Assert.ThrowsException(() => new IndexedQueue(-1)); + } + + [TestMethod] + public void TestQueue() + { + var queue = new IndexedQueue(new int[] { 1, 2, 3 }); + queue.Count.Should().Be(3); + + queue.Enqueue(4); + queue.Count.Should().Be(4); + queue.Peek().Should().Be(1); + queue.TryPeek(out var a).Should().BeTrue(); + a.Should().Be(1); + + queue[0].Should().Be(1); + queue[1].Should().Be(2); + queue[2].Should().Be(3); + queue.Dequeue().Should().Be(1); + queue.Dequeue().Should().Be(2); + queue.Dequeue().Should().Be(3); + queue[0] = 5; + queue.TryDequeue(out a).Should().BeTrue(); + a.Should().Be(5); + + queue.Enqueue(4); + queue.Clear(); + queue.Count.Should().Be(0); + } + + [TestMethod] + public void TestEnumerator() + { + int[] arr = new int[3] { 1, 2, 3 }; + var queue = new IndexedQueue(arr); + + arr.SequenceEqual(queue).Should().BeTrue(); + } + + [TestMethod] + public void TestCopyTo() + { + int[] arr = new int[3]; + var queue = new IndexedQueue(new int[] { 1, 2, 3 }); + + Assert.ThrowsException(() => queue.CopyTo(null, 0)); + Assert.ThrowsException(() => queue.CopyTo(arr, -1)); + Assert.ThrowsException(() => queue.CopyTo(arr, 2)); + + queue.CopyTo(arr, 0); + + arr[0].Should().Be(1); + arr[1].Should().Be(2); + arr[2].Should().Be(3); + + arr = queue.ToArray(); + + arr[0].Should().Be(1); + arr[1].Should().Be(2); + arr[2].Should().Be(3); + } + } +} diff --git a/tests/neo.UnitTests/Network/P2P/UT_TaskSession.cs b/tests/neo.UnitTests/Network/P2P/UT_TaskSession.cs index 6ae403a946..bee2bc8efe 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_TaskSession.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_TaskSession.cs @@ -13,25 +13,19 @@ public class UT_TaskSession [TestMethod] public void CreateTest() { - Assert.ThrowsException(() => new TaskSession(null)); - var ses = new TaskSession(new VersionPayload() { Capabilities = new NodeCapability[] { new FullNodeCapability(123) } }); - Assert.IsTrue(ses.IsFullNode); + Assert.IsFalse(ses.HasTooManyTasks); Assert.AreEqual((uint)123, ses.LastBlockIndex); Assert.AreEqual(0, ses.IndexTasks.Count); - Assert.AreEqual(0, ses.InvTasks.Count); - Assert.AreEqual((uint)0, ses.TimeoutTimes); - Assert.AreEqual((uint)0, ses.InvalidBlockCount); + Assert.IsTrue(ses.IsFullNode); - ses = new TaskSession(new VersionPayload() { Capabilities = new NodeCapability[0] }); + ses = new TaskSession(new VersionPayload() { Capabilities = Array.Empty() }); - Assert.IsFalse(ses.IsFullNode); + Assert.IsFalse(ses.HasTooManyTasks); Assert.AreEqual((uint)0, ses.LastBlockIndex); Assert.AreEqual(0, ses.IndexTasks.Count); - Assert.AreEqual(0, ses.InvTasks.Count); - Assert.AreEqual((uint)0, ses.TimeoutTimes); - Assert.AreEqual((uint)0, ses.InvalidBlockCount); + Assert.IsFalse(ses.IsFullNode); } } } diff --git a/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs b/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs index 5feff2b677..23ac57e261 100644 --- a/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs +++ b/tests/neo.UnitTests/Persistence/UT_MemoryStore.cs @@ -1,5 +1,4 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Caching; using Neo.Persistence; using System.Linq; diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs index 806c4d4817..a9ccec7769 100644 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs +++ b/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs @@ -1,6 +1,5 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Ledger; using Neo.SmartContract; using Neo.SmartContract.Iterators; using Neo.VM.Types; diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs index b10747df8d..ca15234c9b 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/neo.UnitTests/UT_DataCache.cs @@ -1,5 +1,4 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Caching; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; From 604541c91b3151ee40a8613cb8534ebc0540fd27 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 3 Feb 2021 16:50:57 +0800 Subject: [PATCH 65/65] Preview5 (#2288) --- src/neo/neo.csproj | 10 +++++----- tests/neo.UnitTests/neo.UnitTests.csproj | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 224a8fde2b..73789a7ad7 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -1,10 +1,10 @@ - 2015-2020 The Neo Project + 2015-2021 The Neo Project Neo 3.0.0 - preview4 + preview5 The Neo Project net5.0 true @@ -25,11 +25,11 @@ - + - - + + diff --git a/tests/neo.UnitTests/neo.UnitTests.csproj b/tests/neo.UnitTests/neo.UnitTests.csproj index 32b91b429b..9809eb5cbf 100644 --- a/tests/neo.UnitTests/neo.UnitTests.csproj +++ b/tests/neo.UnitTests/neo.UnitTests.csproj @@ -15,13 +15,13 @@ + + - + + - - -